日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

在?React?項目中全量使用?Hooks的方法_React

作者:清風無影Q ? 更新時間: 2022-11-22 編程語言

前言

此篇文章整理了在 React 項目開發中常用的一些 Hooks

React Hooks

Hooks 只能用于函數組件當中

useState

import { useState } from 'react';

const Component = () => {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>click</button>
  )
}

此方法會返回兩個值:當期狀態和更新狀態的函數。效果同 this.state this.setState,區別是 useState 傳入的值并不一定要對象,并且在更新的時候不會把當前的 state 與舊的 state 合并。

useReducer

useReducer 接收兩個參數,第一個是 reducer 函數,通過該函數可以更新 state,第二個參數為 state 的初始值,是 useReducer 返回的數組的第一個值,也是在 reducer 函數第一次被調用時傳入的一個參數。

基礎用法

import { useState } from 'react';

const Component = () => {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>click</button>
  )
}

在基礎用法中,返回一個 dispatch 通過 dispatch 觸發不同的 action 來加減 state。這里既然能傳string action 那么肯定也能傳遞更復雜的參數來面對更復雜的場景。

進階用法

import { useReducer } from 'react';

const Component = () => {
  const [userInfo, dispatch] = useReducer(
    (state, { type, payload }) => {
      switch (type) {
        case 'setName':
          return {
            ...state,
            name: payload
          };
        case 'setAge':
          return {
            ...state,
            age: payload
          };
      }
    },
    {
      name: 'Jace',
      age: 18
    }
  );

  return (
    <button onClick={() => dispatch({ type: 'setName', payload: 'John' })}>
      click
    </button>
  );
};

useContext

在上述案例 useReducer 中,我們將函數的參數改為一個對象,分別有typepayload 兩個參數,type 用來決定更新什么數據,payload 則是更新的數據。寫過 react-redux 的同學可能發這個 reducer 與 react-redux 中的 reducer 很像,我們借助 react-redux 的思想可以實現一個對象部分更改的 reducer ,那么我們便可以使用 React Hooks 的 useContext 來實現一個狀態管理。

import { useMemo, createContext, useContext, useReducer } from 'react';

const store = createContext([]);

const App = () => {
  const reducerValue = useReducer(
    (state, { type, payload }) => {
      switch (type) {
        case 'setName':
          return {
            ...state,
            name: payload
          };
        case 'setAge':
          return {
            ...state,
            age: payload
          };
      }
    },
    {
      name: 'Jace',
      age: 18
    }
  );
  const [state, dispatch] = reducerValue;
  
  const storeValue = useMemo(() => reducerValue, reducerValue);
  
  return (
    <store.Provider value={storeValue}>
      <Child />
    </store.Provider>
  );
};

const Child = () => {
  const [state, dispatch] = useContext(store); // 在子組件中使用
  console.log(state);
  return (
    <button onClick={() => dispatch({ type: 'setName', payload: 'John' })}>
      click
    </button>
  );
}

useEffect

import { 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); // 組件卸載、useEffect 更新 移除計時器
    };
  }, [count]);
  
  // ...
}

如果 useEffect 第二個參數數組內的值發生了變化,那么useEffect第一個參數的回調將會被再執行一遍,這里要注意的useEffect 的返回值函數并不只是再組件卸載的時候執行,而是在這個 useEffect 被更新的時候也會調用,例如上述 count 發生變化后,useEffect 返回的方法也會被執行,具體原因見Using the Effect Hook – React (reactjs.org)

useLayoutEffect

useLayoutEffect useEffect 的API相同,區別:useEffect 在瀏覽器渲染后執行,useLayoutEffect 在瀏覽器渲染之前執行,由于JS是單線程,所以 useLayoutEffect 還會阻塞瀏覽器的渲染。區別就是這,那么應用場景肯定是從區別中得到的,useLayoutEffect 在渲染前執行,也就是說我們如果有狀態變了需要依據該狀態來操作DOM,為了避免狀態變化導致組件渲染,然后更新 DOM 后又渲染,給用戶肉眼能看到的閃爍,我們可以在這種情況下使用 useLayoutEffect

當然這個不只是狀態的改變,在任何導致組件重新渲染,而且又要改變 DOM 的情況下都是 useLayoutEffect 的使用場景。當然這種場景不多,useLayoutEffect 也不能多用,且使用時同步操作時長不能過長,不然會給用戶帶來明顯的卡頓。

useRef

細心的同學有可能發現我在上面寫 useEffect 中有一個 timer 變量,我將其定義在了函數組件外面,這樣寫簡單使用是沒問題的,但是如果該組件在同一頁面有多個實例,那么組件外部的這個變量將會成共用的,會帶來一個沖突,所以我們需要一個能在函數組件聲明周期內部的變量,可以使用 useState 中的 state 但是 state 發生變化組件也會隨之刷新,在有些情況是不需要刷新的,只是想單純的存一個值,例如計時器的 timer 以及子組件的 Ref 實例等等。

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

const Compnent = () => {
  const timer = useRef(null);
  const [count, setCount] = useState(0);

  useEffect(() => {
    clearInterval(timer.current);
    timer.current = setTimeout(() => {
      setCount(count + 1);
    }, 1000);
  }, [count]);

  return <div>UseRef count: {count}</div>;
}

useRef 只接受一個參數,就是 初始值,之后可以通過賦值 ref.current 來更改,我們可以將一些不影響組件聲明周期的參數放在 ref 中,還可以將 ref 直接傳遞給子組件 子元素。

const ref = useRef();

<div ref={ref}>Hello</div>
// or
<Child ref={ref} />

或許有同學這時候會想到,當子組件為 Class 組件時,ref 獲取的是 Class 組件的實例,上面包含 Class 的所有方法屬性等。但當子組件為 Function 組件時,ref 能拿到什么,總不可能是 function 內定義的方法、變量。

useImperativeHandle

import React, { useRef, useState, useImperativeHandle } from 'react';

const App = () => {
  const ref = useRef();
  return (
      <Child ref={ref} />
  );
};

const Child = React.forwardRef((props, ref) => {
  const inputRef = useRef();
  const [value, setValue] = useState(1);
  
  useImperativeHandle(ref, () => ({
    value, // 內部變量
    setValue, // 方法
    input: inputRef.current // Ref
  }));
  
  return (
    <input value={value} inputRef={inputRef} />
  );
})

使用 useImperativeHandle 鉤子可以自定義將子組件中任何的變量,掛載到 ref 上。React.forwardRef 方法可以讓組件能接收到 ref ,然后再使用或者透傳到更下層。

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 的邏輯也就只會執行一次,從而性能。

React 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>
  )
}

useLocation

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

const Compnent = () => {
  const location = useLocation();
  
  useEffect(() => {
    // ...
  }, [location])
}

URL一發生變化,將返回新的 location ,一般可以用來監聽 location.search

useParams

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

const Component = () => {
  const params = useParams();
  
  const getUserInfo = id => { // request api
    // some event
  };
  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,適用于判斷一些全局性組件在不同路由下差異化的展示。

參考

React Hooks

React Redux Hooks

React Router Hooks

結語

使用 Hooks 能為開發提升不少效率,但并不代表就要拋棄 Class Component,依舊還有很多場景我們還得用到它,比如需要封裝一個公共的可繼承的組件,當然通過自定義 hooks 也能將一些共用的邏輯進行封裝,以便再多個組件內共用。

下期更新在React 中自定義 Hooks 的應用場景 ,主要講一些 Hooks 的高階應用

原文鏈接:https://blog.csdn.net/qq_37215621/article/details/127235699

欄目分類
最近更新