跳至主要內容

useEffect

njrreactreact corehookseffect hooks大约 5 分钟约 1510 字

useEffect 可以将组件与外部系统同步。

相关信息

外部系统,包括网络、浏览器 API (setTimeout...)、第三方库或浏览器 DOM。

useEffect(setup, dependencies?)

setup

包含 Effect 逻辑的函数。该 设置函数 可以返回一个 清理函数。大致运行逻辑如下:

  1. 当组件添加到 DOM 时,React 将运行「设置函数」。
  2. 在每次重新渲染依赖关系发生变化后:
    • React 将首先使用 旧值 运行「清理函数」;
    • 然后使用 新值 运行「设置函数」。
  3. 从 DOM 中移除组件后,React 将最后一次运行「清理函数」。

dependencies

setup 代码中依赖的所有响应值的列表。

响应值包括 propsstate 以及直接在组件主体中声明的所有变量和函数。

提示

如果为 React 配置了 linteropen in new window,它就会验证是否将每个反应值都正确指定为依赖项。

依赖项列表必须具有恒定的项数,并以 [dep1、dep2、dep3] 这样的内联方式书写。

React 会使用 Object.isopen in new window 比较法将每个依赖项与其前一个值进行比较。

如果省略此参数,每次重新渲染组件后,Effect 都会重新运行。

传入一个依赖数组

如果指定了依赖关系,「Effect」就会在初始渲染后运行,并在更改依赖关系后重新渲染。

useEffect(() => {
  // ...
}, [a, b]) // Runs again if a or b are different

在下面的示例中,serverUrlroomId 都是响应值,所以必须将它们指定为依赖关系。

因此,在下拉菜单中选择不同的房间或编辑服务器 URL 输入会导致聊天重新连接。

但是,由于「Effect」中没有使用 message(因此它不是依赖项),所以编辑 message 不会重新连接到聊天。

传入一个依赖数组
const { useState, useEffect } = React

function createConnection(serverUrl, roomId) {
  // A real implementation would actually connect to the server
  return {
    connect() {
      console.log(
        '✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'
      )
    },
    disconnect() {
      console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl)
    }
  }
}

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234')
  const [message, setMessage] = useState('')

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId)
    connection.connect()
    return () => {
      connection.disconnect()
    }
  }, [serverUrl, roomId])

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={(e) => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
      <label>
        Your message:{' '}
        <input value={message} onChange={(e) => setMessage(e.target.value)} />
      </label>
    </>
  )
}

export default function App() {
  const [show, setShow] = useState(false)
  const [roomId, setRoomId] = useState('general')
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select value={roomId} onChange={(e) => setRoomId(e.target.value)}>
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
        <button onClick={() => setShow(!show)}>{show ? 'Close chat' : 'Open chat'}</button>
      </label>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  )
}

useEffect 完整指南

useEffect 完整指南open in new window

每一次渲染都有自己的 props 和 state

和 Vue 不同,Vue 的基本原理是数据绑定、观察者模式、proxy。

React 在状态变更时会重新渲染组件,状态并没有任何的数据绑定。

每一次组件调用引起的渲染,props 和 state 都独立于其他渲染。

每一次渲染都有自己的事件处理程序

事件处理函数会记住当前渲染的 propsstate,从这个角度来说可以印证第一点。

每一次渲染都有自己的 Effects

每次渲染都是一个 不同的函数 ,每一个 effect 版本「看到」的 propsstate 值都来自于它属于的那次特定渲染。

从官方文档中的描述中也可以确认这一点:在每次重新渲染依赖关系发生变化后,React 将首先使用旧值运行清理函数,然后使用新值运行设置函数。

每一次渲染的任何东西都是独立的。**在组件内什么时候去读取 props 或者 state 是无关紧要的。**因为在单次渲染的范围内,props 和 state 始终保持不变。