只有資深的 React 工程師才知道的事情

, , , ,

React 對初學者來說可能會有些棘手。然而,理解一些底層原理或技巧可以讓成為高級 React 工程師變得更容易。

1. useEffect 的清理回調函數在每次渲染時都會執行

大多數人認為它只在組件卸載時執行,但這並不正確。

在每次渲染時,來自前一次渲染的清理回調會在下一個效果執行之前執行

讓我們來看一個例子:

function SomeComponent() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    console.log('The current count is ', count)
    return () => {
      console.log('The previous count is ', count)
    }
  })

  return <button onClick={() => setCount(count + 1)}>Click me</button>
}

這會記錄以下內容:

// Component mounts
The current count is 0

// Click
The previous count is 0
The current count is 1

// Click
The previous count is 1
The current count is 2

// Component unmounts
The previous count is 2

這對於創建「取消訂閱然後立即重新訂閱」的模式非常有用,這也是 useEffect 唯一應該被使用的情況。

當然,如果我們添加了依賴數組,這些東西只會在依賴項改變時被調用。

2. useEffect 是一個低階工具,應該僅在類似庫的程式碼中使用

初級 React 開發者經常在不必要的情況下使用 useEffect,這可能會使程式碼更加複雜,產生閃爍或微妙的錯誤。

最常見的情況是同步不同的 useStates,而實際上你只需要一個 useState:

function MyComponent() {
  const [text, setText] = useState("Lorem ipsum dolor sit amet")

  // You don't need to do this !!!
  const [trimmedText, setTrimmedText] = useState("Lorem ip...")

  useEffect(() => {
    setTrimmedText(text.slice(0, 8) + '...')
  }, [text])
}


function MyBetterComponent() {
  const [text, setText] = useState("Lorem ipsum dolor sit amet")

  // Do this instead:
  // (each time text changes, the component will re-render so trimmedText
  //  will be up-to-date)
  const trimmedText = text.slice(0, 8) + '...'
}

3. 使用 key 屬性來重置內部狀態

當元素上的 key prop 發生變化時,該元素的渲染不會被解釋為更新,而是被視為卸載並重新掛載一個具有全新狀態的全新組件實例。

function Layout({ currentItem }) {
  /* When currentItem changes, we want any useState inside <EditForm/>
     to be reset to a new initial value corresponding to the new item */
  return (
    <EditForm
      item={currentItem}
      key={currentItem.id}
    />
  )
}

4. 不要將伺服器狀態放在 useState 中

伺服器狀態大致上是你的資料庫在頁面加載時存在於前端記憶體中的一個快照。

通常由伺服器狀態管理器管理,例如 react-query 或 Apollo。

如果你將其中任何一部分放入 `useState`,當查詢刷新或發生變異時,`useState` 的內容將不會更新。

5. ReactElement 與 ReactNode

ReactElement 僅代表一個標記片段,而 ReactNode 可以是任何 React 可以渲染的東西,例如 ReactElement,但也包括 stringnumberbooleanarraynullundefined 等。

// this is a ReactElement
const a = <div/>

// these are ReactNodes
1
"hello"
<div/>
[2, <span/>]
null

始終將children屬性類型設為ReactNode,這樣你就不會限制該組件可以放入什麼樣的子元素。

JSX.Element 是 TypeScript 的一個內部功能(並非由 React 庫定義),主要針對庫開發者。除此之外,它等同於 ReactElement