react hooks 學習筆記

這篇爲react hooks的學習筆記,僅對須要關注的重點作了記錄。有關react hooks詳細說明,參見官網https://reactjs.org/docs/hook...html

Rules of Hooks

  • 不能將 hooks 放在循環、條件語句或者嵌套方法內。react是根據hooks出現順序來記錄對應狀態的
  • 只在 function 組件和自定義 hooks 中使用 hooks。
  • 命名規範react

    • useState 返回數組的第二項以 set 開頭(僅做爲約定)
    • 自定義 hooks 以 use 開頭(可被 lint 校驗)

API

  • useState
    useState 可傳任意類型的變量或者返回任意類型變量的 function。
    useState 返回數組的第二個參數(setter),可傳任意類型的變量,或者一個接收 state 舊值的 function,其返回值做爲 state 新值redux

    function Counter({ initialCount }) {
      const [count, setCount] = useState(initialCount);
      // Lazy initialization
      const [state, setState] = useState(() => {
        const initialState = someExpensiveComputation(props);
        return initialState;
      });
      return (
        <>
          Count: {count}
          <button onClick={() => setCount(0)}>Reset</button>
          <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
          <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
        </>
      );
    }

    set 方法不會像 setState 同樣作 merge,因此建議若是數據結構簡單,能夠將變量根據數據結構須要放在不一樣的 useState 中,避免大量使用相似{...state, value}形勢。若是數據結構複雜,建議使用 useReducer 管理組件的 state數組

  • useEffect瀏覽器

    useEffect(effect, array);

    effect 函數將在 componentDidAmount 時觸發和 componentDidUpdate 時有條件觸發。能夠返回一個函數(returnFunction),returnFunction 將會在 componentWillUnmount 時觸發和在 componentDidUpdate 時先於 effect 有條件觸發。
    與 componentDidAmount 和 componentDidUpdate 不一樣之處是,effect 函數觸發時間爲在瀏覽器完成渲染以後。 若是須要在渲染以前觸發,須要使用 useLayoutEffect。
    第二個參數 array 做爲有條件觸發狀況時的條件限制。數據結構

    • 若是不傳,則每次 componentDidUpdate 時都會先觸發 returnFunction(若是存在),再觸發 effect。
    • 若是爲空數組[],componentDidUpdate 時不會觸發 returnFunction 和 effect。
    • 若是隻須要在指定變量變動時觸發 returnFunction 和 effect,將該變量放入數組。
  • useContext
    和consumer相似,仍然須要與Provider配合使用ide

    const Context = React.createContext('light');
    
    // Provider
    class Provider extends Component {
      render() {
        return (
          <Context.Provider value={'dark'}>
            <DeepTree />
          </Context.Provider>
        )
      }
    }
    // Consumer
    function Consumer(props) {
      const context = useContext(Context);
      return (
        <div>
          {context} // dark
        </div>
      );
    }
  • useReducer
    用於管理複雜結構的狀態對象,與redux的核心邏輯一致。函數

    const [state, dispatch] = useReducer(reducer, initialState, {
      type: 'reset',
      payload: initialCount
    });
    // demo
    const TodosDispatch = React.createContext(null);
    
    function TodosApp() {
      // Tip: `dispatch` won't change between re-renders
      const [todos, dispatch] = useReducer(todosReducer, initialState);
    
      return (
        <TodosDispatch.Provider value={dispatch}>
          <DeepTree todos={todos} />
        </TodosDispatch.Provider>
      );
    }
  • useCallback
    useCallback和下面的useMemo是很是實用的提高性能的小工具。工具

    const memoizedCallback = useCallback(
      () => {
        doSomething(a, b);
      },
      [a, b]
    );

    useCallback返回一個memoized函數,在參數a和b都沒有改變時,老是返回同一個函數。
    其具體實現邏輯基本以下:性能

    let memoizedState = null;
    function useCallback(callback, inputs) {
      const nextInputs =
        inputs !== undefined && inputs !== null ? inputs : [callback];
      const prevState = memoizedState;
      if (prevState !== null) {
        const prevInputs = prevState[1];
        if (areHookInputsEqual(nextInputs, prevInputs)) {
          return prevState[0];
        }
      }
      memoizedState = [callback, nextInputs];
      return callback;
    }

    注:第二個參數目前只用於指定須要判斷是否變化的參數,並不會做爲形參傳入回調函數。建議回調函數中使用到的變量都應該在數組中列出。之後的版本可能會將第二項數組參數移除,自動判斷回調函數中使用到的變量是否變化來判斷返回結果。

  • useMemo

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

    與useCallback相似,返回一個memoized函數執行結果。
    useMemo(() => fn, inputs) 等價於 useCallback(fn, inputs)
    useMemo可用於實現PureComponent子組件:

    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 (
        <>
          {child1}
          {child2}
        </>
      )
    }
  • useRef

    const refContainer = useRef(initialValue);

    相似 react.createRef()。
    在使用hooks的function component中,useRef不只能夠用來作DOM的引用,還能夠作來做爲相似class的實例屬性,由於相同位置的useRef()每次返回的都是同一個對象。

    function Timer() {
      const intervalRef = useRef();
    
      useEffect(() => {
        const id = setInterval(() => {
          // ...
        });
        intervalRef.current = id;
        return () => {
          clearInterval(intervalRef.current);
        };
      });
    
      // ...
    }

    利用useRef()的這種特性,還能夠作不少其餘有趣的事情,例如獲取previous props或previous state:

    function Counter() {
      const [count, setCount] = useState(0);
    
      const prevCountRef = useRef();
      useEffect(() => {
        prevCountRef.current = count;
      });
      const prevCount = prevCountRef.current;
    
      return <h1>Now: {count}, before: {prevCount}</h1>;
    }
  • useImperativeMethods

    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeMethods(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} ... />;
    }
    FancyInput =  React.forwardRef(FancyInput);

    useImperativeMethods提供了父組件直接調用子組件實例方法的能力。
    上例中,一個包含 <FancyInput ref={fancyInputRef} /> 的父組件,就能夠調用 fancyInputRef.current.focus().

Tips

  • 目前暫時沒有與getSnapshotBeforeUpdate和componentDidCatch相匹配的hooks。
  • 使用ESlint plugin(eslint-plugin-react-hooks)作代碼校驗。
  • Hooks的調用只能放在function component(首字母大寫的function)或者自定義Hook(名爲useXxxx的function)中。
  • Hooks在組件的每次render時都以徹底相同的順序執行。
  • 用React.memo來實現shouldComponentUpdate。

    const Button = React.memo((props) => {
      // your component
    });
  • 請將refs理解成class中的實例屬性。
相關文章
相關標籤/搜索