从新版 React 文档中学到的

从新版 React 文档中学到的

React Docs Beta

React 基础

  • JSX 大括号:获取全局变量的窗口

  • Props 是一个只读快照,每次 render 都接收一个新的 props 对象

  • Event handlers、useEffect 中执行副作用

  • 3 类输入数据 props、state、context,把他们当成只读的

  • React 的严格模式,在开发时对每个组件调用两次 render,用来发现有副作用的组件,第二次调用的结果会被舍弃

  • 组件更新的两个原因:调用 ReactDOM.render 触发的初始 render、state 更新 rerender

  • Rendering 就是 React 调用我们的组件,初始 render 是调用根组件,后续 render 是调用 state 更新的组件

  • Render function 需要是纯函数

  • 将变化提交到 DOM,初始 render 是调用 appendChild、后续是最小更新集

  • React 会等待 event handlers 中的代码都执行完,才会处理状态更新

  • React 不会跨事件进行批处理,每个事件单独处理

State

基础

  • 触发 state 更新的两类原因:用户输入(点击按钮、输入、跳转)、电脑输入(网络请求返回、计时器完成、图片加载完成)

  • 用户输入一般需要 Event handler 处理

  • state 更新队列内部实现

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  for (let update of queue) {
    if (typeof update === 'function') {
      // Apply the updater function.
      finalState = update(finalState);
    } else {
      // Replace the next state.、
      finalState = update;
    }
  }
  
  return finalState;
}
复制代码

原则(方便更新、减少错误

  1. 组合有关系的 state 变量:如果你总是同时更新一些 state 变量,考虑把他们组合成一个 state 变量;或者不知道 state 变量的数量时,比如表单数据

  2. 避免互斥的 state 变量:如果一些 state 变量之间互斥,就容易出现错误,避免这种情况 

  3. 避免冗余的 state 变量:如果是可以根据现有 state 变量、props、url、cookie、localStorage 等计算出来的信息,就不需要放在 state 中;不会改变的值、props 传递的值不要放在 state 中

  4. 避免重复的 state 变量:如果相同信息在多个 state 变量间、url、storage 中重复,就很难去让他们保持同步。所以要避免重复的 state 变量

  5. 避免嵌套很深的 state 变量:嵌套过深的 state 变量不方便更新。所以最好保持 state 结果扁平

  6. 对于选中条目,使用 selectedId 或 selectedIndex,而不是选择条目这个对象

  7. 保证 state 变量的单一来源

使用

  • 删除任何不必要的 state: 这个 state 是不是另一个 state 的子集;能不能从另一个 state 中获取同样信息

state 保留和重置

  • React 只会保留正在 UI 树中渲染的组件的 state。组件一旦从 UI 树中移除,React 就会销毁这个组件,丢弃他的状态

  • UI 树中同一个位置、同一个组件,React 会把他看作一个组件

  • 同一位置不同组件,state 会被重置,这个组件的子树也会被销毁、重建

  • 不应该嵌套定义组件函数,如果嵌套定义的话,每次外层 render,都会声明不同的组件,影响 React 判断。所以一定要在顶层声明组件函数

  • 不让同一位置、同类型组件保留状态:让他们在不同位置、加 key

Reducer & Context

  • useReducer:reducer 函数中的 case 使用大括号并 return 回新的状态

  • reducer 的名字来源于 Array.reduce 的参数名,都叫 reducer,根据结果和当前数据返回下一个结果

  • reducer 是把 state 更新的逻辑单独抽离出来

  • reducer 应该是一个纯函数,不能有副作用

  • action 的名字应该是描述发生了什么

  • Context 就像 CSS 属性的继承,只能被更低层级 Provider 的值覆盖 

  • Context 使用场景:主题、当前用户信息、路由、全局状态管理

  • Context 和 Reducer 一起使用,放到一个文件中,包括:

    1. reducer 及初始 state
    2. 创建 context 的逻辑
    3. 使用 children 作为 props 的 Provider 组件
    4. 导出 useSomeContext 函数

Ref

  • ref: 希望组件记住信息,又不希望这些信息变化后触发 rerender

  • ref 更新不会造成组件 rerender

  • 如果信息用来 render,放在 state 中;如果信息只是 event handler 需要,且不会触发 render,使用 ref

  • 不要在 rendering 时读写 ref.current,如果需要就使用 state。因为 React 感知不到 ref.current 的变化 

  • ref 内部实现

// Inside of React
function useRef(initialValue) {
  const [ref, unused] = useState({ current: initialValue });
  return ref;
}
复制代码
  • ref 使用场景,处理 React 之外的交互:存储 timeout IDs、存储操作 DOM 元素、存储不参与 JSX  计算的信息

  • ref 的变化会及时更新,和 state 每次 render 中的快照不同。因为 ref 就是普通的 JS 对象

  • ref callback 可以管理多个 refs

  • forwardRef 就是把父组件的 ref 属性传递给子组件

  • useImperativeHandle 让你可以自定义提供给父组件的 ref 对象

  • flushSync 强制 React 同步更新 DOM

Hooks

基础

  • Hooks 的本质就是提供了让函数组件能够绑定到某个可变的数据源的能力

  • 每一次 UI 的变化,都是通过重新执行整个 Hooks 函数来完成的

  • Hooks 函数体中的代码,直接影响当次 render 的结果

  • Hooks 和普通函数的区别是普通函数中有没有用到其它 Hooks

内置 Hooks 解决了什么问题

  • useState 用于保存状态

  • useEffect 用于执行副作用

  • useCallback 用于缓存函数

  • useMemo 用于缓存计算结果

  • useRef 用于存储跨渲染的数据

  • useReducer 将状态更新逻辑抽离出来

具体 Hooks

useState

  • const [state, setState] = useState('')setState() 的时候,re-render,整个 Hooks 函数重新执行

  • 惰性初始化 state:初始化函数只在初始渲染时被调用,避免复杂计算性能开销

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});
复制代码

useEffect:每次组件 render 完后判断依赖并执行相应的副作用

参数
  • useEffect 接收一个函数作为第一个参数,React 在 DOM 更新后调用这个函数

  • 第一个参数不能是 async/await 函数,因为该语法默认返回一个 promise

  • 第一个参数中,可以返回一个清除 effect 的回调函数,这个回调函数不只是会在组件销毁时执行,而且是每次 Effect 重新执行之前都会执行,用于清理上一次 Effect 的执行结果

  • useEffect 接受一个数组依赖项作为第二个参数,控制 effect 的条件执行

依赖项:避免执行无意义的工作
  • useEffect(() => {})未声明第二个依赖项。每次 render 都执行

  • useEffect(() => {}, [])声明一个空数组作为依赖项。仅第一次 render 后执行

  • useEffect(() => {}, [deps])声明依赖项数组。第一次以及依赖项发生变化后执行

  • useEffect() => { return () => {} }, [])声明依赖项数组,返回一个回调函数。仅第一次 render 后执行 effect,组件 unmount 后执行回调函数

useCallback

  • useCallback(fn, [deps])只有当某个依赖变量发生变化时,才会重新声明 fn 这个回调函数

  • <CustomCompnent callback={fn} />避免子组件的重复渲染

useMemo

  • useMemo(() => computeExpensiveValue(a, b), [a, b])只有当某个依赖变量发生变化时,才会重新计算

useRef:

  • 在多次渲染之间共享数据,存储跨渲染的数据

  • 使用 useRef 保存的数据一般是和 UI 的渲染无关的,因此当 ref 的值发生变化时,是不会触发组件的重新渲染的,这也是 useRef 区别于 useState 的地方

  • 把变量定义到组件外,如果一个页面上有多个组件实例,组件外的普通变量是被共享的,可能会产生问题

其他

  • React 会使用浅比较来对比依赖项是否发生了变化,所以要特别注意数组或者对象类型。如果你是每次创建一个新对象,即使和之前的值是等价的,也会被认为是依赖项发生了变化。这是一个刚开始使用 Hooks 时很容易导致 Bug 的地方

  • 写组件的顺序:简单项目从顶层到底层,复杂项目从底层到顶层

猜你喜欢

转载自juejin.im/post/7041207365072322596
今日推荐