先下手爲強,在 fre 中瘋狂 suspense

halo,你們好,我是 132,今天閒着沒事,對 suspense 和 context 進行了封裝react

由於我一直對尺寸有超高的強迫症,因此 fre core 只提供最最最核心的機制git

好比,我提供 promise 的內部捕獲,但不提供 Suspense 組件的封裝github

好比我提供 state ,但不提供 contextjson

可是不少人會質疑,基於現有的 API 和 機制,到底能不能本身封裝出來api

答案是,of course!promise

不信,我封裝給你看——bash

Suspense

suspense 的本質就是在外部 throw promise,而後由 fre 內部捕獲到,而後打斷調和,等 resolve 後,在繼續調和閉包

react 目前的 suspense 還不支持數據請求,只支持異步組件框架

既然 react 不支持,那我就封裝一個異步

export function createSuspense(promise) {
  let pending = true
  let result
  let currentState = null

  return state => {
    if (currentState !== state) {
      pending = true
      currentState = state
    }
    if (pending) {
      throw promise(state).then(res => {
        pending = false
        result = res
      })
    } else {
      return result
    }
  }
}
複製代碼

如上,createSuspense 接收 promise 函數,它返回一個閉包,這個函數負責把 promise throw 出去

它這麼用:

function fetchUsers (pageSize) {
  console.log('fetch users from clicli...')
  return fetch(`https://api.clicli.us/users?level=4&page=1&pageSize=${pageSize}`)
    .then(res => res.json())
    .then(data => {
      return data.users
    })
}

const useUser = createSuspense(fetchUsers)
複製代碼

咱們將返回的函數做爲 hook,能夠隨意用到任何組件

function App () {
  const [pageSize, setPageSize] = useState(1)
  const users = useUser(pageSize)

  return (
    <main>
      <h1>Fetching clicli users</h1>
      <button onClick={() => setPageSize(pageSize + 1)}>Click {pageSize}</button>
      {users.map(user => (
        <img src={`https://q1.qlogo.cn/g?b=qq&nk=${user.qq}&s=640`} />
      ))}
    </main>
  )
}
複製代碼

這樣一來,就能夠愉快的使用 suspense 進行數據請求了

目前 react 尚未肯定相似的 API,目前的 Suspense 組件是用來控制 lazy 組件的

這類組件的 API,好處是嵌套,壞處就是複用

因此綜上所述,我提了這個 rfc:github.com/reactjs/rfc…

若是反響好的話,我就嘗試提 pr 試試::>_<::

可是無論怎麼說,這個實現是沒問題的,至少是 fre ,我能給出的最合適的實現了

Context

結合上面的 Suspense API,咱們發現了一個 hooks 封裝的經常使用套路:返回閉包

使用一樣的套路,咱們能夠 20 行封裝出 Context API

export function createContext(defaultValue) {
  const listeners = new Set()
  let backupValue = defaultValue

  return () => {
    const [value, setValue] = useState(backupValue)

    useEffect(() => {
      backupValue = value
      listeners.forEach(f => f !== setValue && f(value))
    }, [value])

    useEffect(() => {
      listeners.add(setValue)
      return () => {
        listeners.delete(setValue)
      }
    }, [])

    return [value, setValue]
  }
}
複製代碼

用法是相似的:

const useTheme = createContext('light')

function App() {
  const [theme, setTheme] = useTheme()
  return (
    <div> {theme} <A /> <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}> change </button> </div>
  )
}

function A() {
  const [theme] = useTheme()
  return <div>{theme}</div>
}
複製代碼

如上,hooks 就是這麼靈活,咱們能夠肆意封裝,這些封裝未必須要框架內置,用戶徹底能夠本身搞定

總結

以上的兩個封裝的例子都在 fre 倉庫裏

github.com/132yse/fre/…

github.com/132yse/fre/…

你們有興趣能夠跑用例,或者去 react rfcs 中一塊兒討論哈!

相關文章
相關標籤/搜索