$ ls ~yifei/notes/

学习 React Hooks

Posted on:

Last modified:

useState

useState 可以用来管理一个组件比较简单的一组状态,如果状态多了不适合使用 useState 管理, 可以使用 useReducer.

import React, {useState} from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(c => c + 1)}> Click me </button>
    </div>
  );
}

export default Counter;

setState 在更新函数中可能表现为异步,保险起见,应该使用一个函数作为它的参数,而不是直接 使用 state 值作为参数。比如在上面的例子中:

// 正确
<button onClick={() => setCount(c => c + 1)}> Click me </button>
// 错误
<button onClick={() => setCount(count + 1)}> Click me </button>

还有一个比较常见的模式是使用一个 object 保存多个状态,在部分更新 state 的时候,可以这样做:

const [state, setState] = useState({
    "showText": true,
    "showShadow": true
})

// 使用 {...s, foo: bar} 避免重复代码
<button onClick={() => setState(s => {...s, showText: false})}>Hide Text</button>

useRef

useRef 通常用于保存 DOM 节点的引用

// 创建 ref
const anchor = useRef(null)

function getStyles() {
    // 使用 ref.current 访问引用
    return window.getComputedStyle(anchor.current)
}

// 使用 ref={xxx} 绑定引用
return <a ref={anchor}>hello</a>

useRef 钩子和 useState 有些类似,都用于在不同的重绘之间保存状态。区别也很明显: useState 中的 setState 会导致重绘,而改变 useRef 的 .current 则不会。

useEffect

useEffect hook 用来实现一些副作用,可以在某些变量更新时重绘组件。

默认情况下,每次 state 有改变的时候,都会调用 useEffect 函数。如果需要更改触发的时机, 那么需要使用 useEffect 的第二个参数来指定监听的事件或者说状态。当第二个参数只使用一个空 数组 [] 的时候就只在组件加载的时候调用。数组的变量表示观察这些变量。

import React, {useState, useEffect} from 'react';

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    let interval;
    if (isOn) {
      interval = setInterval(() => console.log('tick'), 1000);
    }
    return () => clearInterval(interval);
  }, [isOn]);
  ...
}

export default App;

在 useEffect 中返回的函数会被用来做垃圾清理。另外,组件初始化的时候总会触发一次 useEffect.

建议把不闭包依赖 props 和 state 的函数提到组件外,只在 effect 中使用的函数放到 effect 里。

useMemo/useCallback

用来保证子组件 props 的 Referential equality,实际很少用到。

useCallback(fn, [])  // is equal to
useMemo(() => fn, [])

网上有些博客中说到的「useCallback 改善性能」大多属于误用。

自定义钩子

通过灵活组合 useState, 和 useEffect, 我们完全可以创建自己的钩子。

import React from 'react';

function useOffline() {
  const [isOffline, setIsOffline] = React.useState(false);

  function onOffline() { setIsOffline(true); }

  function onOnline() { setIsOffline(false); }

  React.useEffect(() => {
    window.addEventListener('offline', onOffline);
    window.addEventListener('online', onOnline);

    return () => {
      window.removeEventListener('offline', onOffline);
      window.removeEventListener('online', onOnline);
    };
  }, []);

  return isOffline;
}

function App() {
  const isOffline = useOffline();

  if (isOffline)
    return <div>Sorry, you are offline ...</div>;

  return <div>You are online!</div>;
}

export default App;

参考

  1. https://www.robinwieruch.de/react-hooks
  2. https://www.robinwieruch.de/react-hooks-fetch-data
  3. https://medium.com/@nazrhan.mohcine/react-hooks-work-with-usestate-and-usereducer-effectively-471646cdf925
  4. https://daveceddia.com/usecontext-hook/
  5. https://medium.com/@wisecobbler/using-a-function-in-setstate-instead-of-an-object-1f5cfd6e55d1
  6. https://stackoverflow.com/questions/56615931/react-hook-setstate-arguments
  7. https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
  8. https://mp.weixin.qq.com/s/gzwKs-M7ip3ccW3jnYK9tg
  9. https://medium.com/suyeonme/using-usecontext-and-usereducer-together-lets-create-redux-like-global-state-in-react-87470e3ce7fa
  10. https://kentcdodds.com/blog/usememo-and-usecallback
  11. https://stackoverflow.com/questions/56455887/react-usestate-or-useref

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.