Hooks API

作者: 一瓣山河 | 来源:发表于2019-07-28 11:42 被阅读0次

    useEffect

    // deps 是依赖数组,可以为空 
    useEffect(() => {
      console.log('useEffect')
    },  [deps]);
    

    useEffect的使用 参考上篇文章

    执行时机:

    • useEffect可以看做componentDidMount、componentDidUpdate、componentWillUnmount生命周期的结合。
    • 只有当deps里的变量发生改变时,才会执行。当第二参数是空数组,该effect 只会在组件didMount和willUnmount后执行;当第二个参数为空时,组件每次渲染都会执行。

    useMemo

    // 返回一个有记忆的值(memoized)
    const memoizedValue = useMemo(() => computeExpensiveValue(deps),deps);
    

    // useMemo 可以帮助避免子组件的不必要的rerender

    function Parent({ a, b }) {
      // Only re-rendered if `a` changes:
      const child1 = useMemo(() => <Child1 a={a} />, [a]);
      // Only re-rendered if `b` changes:
      const child2 = useMemo(() => <Child2 b={b} />, [b]);
      return (
        <div>
          {child1}
          {child2}
        </div>
      )
    }
    

    执行时机:

    • useMemo组件rendering 的时候执行,在useEffect之前。因此副作用应放在useEffect里,而不是useMemo。
    • 只有当deps发生变化时,useMemo 才会重新计算memoizedValue, 避免渲染时不必要的计算。当第二个参数为空时,组件每次渲染都会执行。

    useCallback

    // 返回一个有记忆的callback
    const memoizedCallback = useCallback(
      () => {
        doSomething(deps);
      },
      [deps],
    );
    

    执行时机:

    • 只有当deps发生变化时,memoizedCallback才会改变。这对通过传递callbacks来优化子组件的渲染很有帮助。(配合子组件的shouldComponentUpdate 或者 React.memo 起到减少不必要的渲染的作用)
    // useCallback 与useMemo
    useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
    

    useCallback 的真正目的还是在于缓存了每次渲染时 inline callback 的实例,这样方便配合子组件的 shouldComponentUpdate 或者 React.memo 起到减少不必要的渲染的作用。

    useCallback 适用于所有的场景吗?
    我们知道useCallback也是有依赖项的,如果一个 callback 依赖于一个经常变化的 state,这个 callback 的引用是无法缓存的。React 文档的 FAQ 里也提到了这个问题,还原一下问题的场景:

    function Form() {
      const [text, setText] = useState('');
    
      const handleSubmit = useCallback(() => {
        console.log(text);
      }, [text]); // 每次 text 变化时 handleSubmit 都会变
    
      return (
        <div>
          <input value={text} onChange={(e) => setText(e.target.value)} />
          <ExpensiveTree onSubmit={handleSubmit} /> // 很重的组件
        </div>
      );
    }
    

    官网提出,如果要记忆化的callback函数是一个事件处理器,rendering时不需要调用,那么,你可以用useRef创建一个实例变量,手动把最新的值存进来。例如:

    function Form() {
      const [text, updateText] = useState('');
      const textRef = useRef();
    
      useEffect(() => {
        textRef.current = text; // Write it to the ref
      });
    
      const handleSubmit = useCallback(() => {
        const currentText = textRef.current; // Read it from the ref
        alert(currentText);
      }, [textRef]); // Don't recreate handleSubmit like [text] would do
    
      return (
        <div>
          <input value={text} onChange={e => updateText(e.target.value)} />
          <ExpensiveTree onSubmit={handleSubmit} />
        </div>
      );
    }
    

    但是,上面的额解决方案也有缺陷,只有在 DOM 更新时才对 ref.current 做更新,会导致在 render 阶段不能调用这个函数。更严重的是,因为对 ref 做了修改,在未来的 React 异步模式下可能会有诡异的情况出现(因此上文中官方的解法也是”异步模式不安全“的)。

    怎么避免深度传递callback?

    考虑到上面提到的弊端,目前推荐的解决方案是使用useReducer,传递dispatch通过context。reducer 其实是在下次 render 时才执行的,所以在 reducer 里,访问到的永远是新的 props 和 state。

    const TodosDispatch = React.createContext(null);
    
    function TodosApp() {
      // useReducer 返回的 dispatch 函数是自带 memoize 的
      // Note: `dispatch` won't change between re-renders
      const [todos, dispatch] = useReducer(todosReducer);
    
      return (
        <TodosDispatch.Provider value={dispatch}>
          <DeepTree todos={todos} />
        </TodosDispatch.Provider>
      );
    }
    

    Any child in the tree inside TodosApp can use the dispatch function to pass actions up to TodosApp:

    function DeepChild(props) {
      // If we want to perform an action, we can get dispatch from context.
      const dispatch = useContext(TodosDispatch);
    
      function handleClick() {
        dispatch({ type: 'add', text: 'hello' });
      }
    
      return (
        <button onClick={handleClick}>Add todo</button>
      );
    }
    

    TodosApp的任何一个子组件都可以使用dispatch函数向TodosApp传递actions.

    如果你想同时把 state 作为 context 传递下去,请分成两个 context 来声明。

    相关文章

      网友评论

          本文标题:Hooks API

          本文链接:https://www.haomeiwen.com/subject/jnmqrctx.html