在 React 專案中全量使用 Hooks

yi個程式猿發表於2020-03-16

此文章只是整理了在 React 專案開發中常用的一些 Hooks,具體每個的坑,以及詳細解刨後續繼續更新,並會加上鍊接。

React Hooks

Hooks 只能用於函式元件當中。

useState

import React, { useState } from 'react';

const Component = () => {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>click<button>
  )
}
複製程式碼

此方法會返回兩個值:當期狀態和更新狀態的函式。效果同 this.statethis.setState,區別是 useState 傳入的值並不一定要物件,並且在更新的時候不會吧當前的 state 與舊的 state 合併。

useEffect

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

let timer = null;

const Component = () => {
  const [count, setCount] = useState(0);
  
  // 類似於 class 元件的 componentDidMount 和 componentDidUpdate:
  useEffect(() => {
    document.title = `You clicked ${count} times`;
    
    timer = setInterval(() => {
      // events ...
    }, 1000)
    
    return () => {
      // 類似 componentWillUnmount
      // unmount events ...
      clearInterval(timer); // 元件解除安裝 移除計時器
    };
  }, [count]);
  
  // ...
}
複製程式碼

如果 useEffect 第二個引數陣列內的值發生了變化,那麼 useEffect 第一個引數的回撥將會被再執行一遍。

useCallback

import React, { useCallback } from 'react';

const Component = () => {
  const setUserInfo = payload => {}; // request api

  const updateUserInfo = useCallback(payload => {
    setUserInfo(Object.assign({}, userInfo, payload));
  }, [userInfo]);
  
  return (
    <UserCard updateUserInfo={updateUserInfo}/>
  )
}
複製程式碼

useCallback 會在二個引數的依賴項發生改變後才重新更新,如果將此函式傳遞到子元件時,每次父元件渲染此函式更新,就會導致子元件也重新渲染,可以通過傳遞第二個引數以避免一些非必要性的渲染。

在一些特殊情況下傳遞此引數還可以解決一些由靜態作用域導致的問題。

useMemo

import React, { useMemo } from 'react';

const Component = () => {
  const [count, setCount] = useState(0);
 
  const sum = useMemo(() => {
    // 求和邏輯
    return sum;
  }, [count]);
  
  return <div>{sum}</div>
}
複製程式碼

useMemo 的用法跟 useCallback 一樣,區別就是一個返回的是快取的方法,一個返回的是快取的值。上述如果依賴值 count 不發生變化,計算 sum 的邏輯也就只會執行一次,從而效能。

這是之後更新的:useCallback 與 useMemo 詳解 歡迎閱讀。

Redux Hooks

useSelector

import { shallowEqual, useSelector } from 'react-redux';

const Component = () => {
  const userInfo = useSelector(state => state.userInfo, shallowEqual);
  
  // ...
}
複製程式碼

useSelector 的第二個引數是一個比較函式,useSelector 中預設使用的是 === 來判斷兩次計算的結果是否相同,如果我們返回的是一個物件,那麼在 useSelector 中每次呼叫都會返回一個新物件,所以所以為了減少一些沒必要的 re-render,我們可以使用一些比較函式,如 react-redux 自帶的 shallowEqual,或者是 Lodash 的 _.isEqual()、Immutable 的比較功能。

useDispatch

import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';

const Compnent = () => {
  const dispatch = useDispatch();
  const clearUserInfo = useCallback(
    () => dispatch({ type: 'clearUserInfo' }),
    [dispatch]
  );
  
  return (
    <button onClick={clearUserInfo}>click</buttn>
  )
}
複製程式碼

使用 dispatch 來排程操作,加上 useCallback 來減少不必要的渲染。

React Router Hooks

useHistory

import { useHistory } from 'react-router';

const Compnent = () => {
  const history = useHistory();
  
  return (
    <button onClick={() => history.push('/home')}>go home</buttn>
  )
}
複製程式碼

useHistory

import React, { useEffect } from 'react';
import { useLocation } from 'react-router';

const Compnent = () => {
  const location = useLocation();
  
  useEffect(() => {
    // ...
  }, [location])
  
  return (
    <button onClick={() => history.push('/home')}>go home</buttn>
  )
}
複製程式碼

URL一發生變化,他將返回新的 location

useParams

import { useParams, useEffect } from 'react-router';

const Component = () => {
  const params = useParams();
  
  const getUserInfo = () => {}; // request api
  
  useEffect(() => {
    // parms 的 uid 發生變化就會重新請求使用者資訊
    getUserInfo(params.uid);
  }, [params.uid]);
  
  // ...
}
複製程式碼

useParams 返回 react-router 的引數鍵值對

useRouteMatch

import { useRouteMatch } from 'react-router';

const Component = () => {
  const match = useRouteMatch('/login');
  
  // ...
}
複製程式碼

useRouteMatch 可以傳入一個引數 path,不傳引數則返回當前路由的引數資訊,如果傳了引數則用來判斷當前路由是否能匹配上傳遞的 path,適用於判斷一些全域性性元件在不同路由下差異化的展示。

結語

使用 Hooks 能為開發提升不少效率,但並不代表就要拋棄 Class Component,依舊還有很多場景我們還得用到它,比如需要封裝一個公共的可繼承的元件...

參考

相關文章