Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(筆記)SWR 原理 #132

Open
gracekrcx opened this issue Jul 14, 2022 · 5 comments
Open

(筆記)SWR 原理 #132

gracekrcx opened this issue Jul 14, 2022 · 5 comments

Comments

@gracekrcx
Copy link
Owner

gracekrcx commented Jul 14, 2022

The name “SWR” is derived from stale-while-revalidate, a HTTP cache invalidation strategy popularized by HTTP RFC 5861.

SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.

TL;DR

  • 如果有 cache 的資料,就先回 cache 資料
  • 然後,打 api 確認資料是否過期
  • 沒過期:沿用資料
  • 過期:(1) 更新 cache (2) 更新畫面資料

stale-while-revalidate

stale-while-revalidate 源自於 HTTP Cache-Control header 的一個屬性

在使用 useSWR 的時候 key 可以傳入

  1. 字串
  2. 陣列
  3. 方法

管理資料

const stateRef = useRef({
 data: ..,
 error: ...,
 isValidating: ...
}) 

處理畫面 rerender link

const rerender = useState(null)[1]

revalidate

這個階段執行 API 的 fetcher 和驗證,然後把執行後的 Promise 裝到 CONCURRENT_PROMISES 裡面

softRevalidate

當 dedupe 設定為 true 時,SWR 會將短時間內相同 key 的 request 合併成一次,避免重複觸發相同 request 。

cache

使用 ES6 的 Map 儲存 api response 資料搭配 get, set

dispatch

比對當前存在 stateRef 的資料,如果新資料和原本的資料相同就直接 continue,不同就更新,並執行 rerender 讓 component 用新的 stateRef 更新畫面
(原碼現在找不到 dispatch)

compare, isEqual

const isEqual = (prev: State<Data, any>, current: State<Data, any>) => {
    let equal = true
    for (const _ in stateDependencies) {
      const t = _ as keyof StateDependencies
      if (!compare(current[t], prev[t])) {
        if (t === 'data' && isUndefined(prev[t])) {
          if (!compare(current[t], fallback)) {
            equal = false
          }
        } else {
          equal = false
        }
      }
    }
    return equal
  }
const compare = (currentData: any, newData: any) =>
  stableHash(currentData) == stableHash(newData)

rAF

SWR 在 revalidate 的地方,處理優先順序避免 block 主要的 rendering

 // Trigger a revalidation.
    if (shouldDoInitialRevalidation) {
      if (isUndefined(data) || IS_SERVER) {
        // Revalidate immediately.
        softRevalidate()
      } else {
        // Delay the revalidate if we have data to return so we won't block
        // rendering.
        rAF(softRevalidate)
      }
    }

Window Focus Refetching

window.addEventListener('focus', revalidate, false);

Observer Pattern

原來 Observer Pattern 才是 SWR 最特別的地方
訂閱特定 key 的所有 component 都會在 key 所對應的資料發生變化時
被通知 rerender

參考
開源專案讀起來 | 聽說可以幫你保管資料再決定要不要更新的 SWR
開源專案讀起來 | 從 SWR 來看看實務上的 Observer Pattern(觀察者模式)
觀察者模式 (Observer Pattern)
了解 SWR 的運作機制,How this async state manager works ?

@gracekrcx
Copy link
Owner Author

gracekrcx commented Jul 16, 2022

import React, { useEffect, useLayoutEffect } from 'react'
export const IS_SERVER = !isWindowDefined || 'Deno' in window
// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser.
export const useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect

folder path: swr/_internal/utils/env.ts

useIsomorphicLayoutEffect (變數宣告)
Isomorphic 同構: 這裡簡單先解讀 client 或 server

// call function
// Logic for updating refs.
 useIsomorphicLayoutEffect(() => {
   fetcherRef.current = fetcher
   configRef.current = config
   ...
 })

code link

@gracekrcx
Copy link
Owner Author

gracekrcx commented Jul 16, 2022

Update

(user 執行動作,觸發資料的變動。例如:user 按下 toggle,背後控制 toggle 狀態的 isOntrue 轉為 false。)

Render

React Update DOM

(React 收到狀態的變更後,利用新的資料畫出 Virtual DOM。)

  • useLayoutEffect 會「同步執行」side effect
DOM diff 之後把修改的部分告訴 Browser,Browser paint screen

(user 看到最終新的畫面。)

  • useEffect

@gracekrcx gracekrcx changed the title SWR 原理 (筆記)SWR 原理 Jul 19, 2022
@gracekrcx
Copy link
Owner Author

  • React Fiber
  • requestIdleCallback
  • requestAnimationFrame

@gracekrcx
Copy link
Owner Author

如果放入 key 的資料是一個 Object 應該使用 useMemo 去控制 rerender 的機制

  const params = useMemo(() => ({ id }), [id]);
  const { data: post } = useSWR(
    ["https://jsonplaceholder.typicode.com/posts/", params],
    fetcher
  );

React SWR | 取得遠端資料的殺手級函式庫

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant