react中的那些Hooks的總結


1、useState

useState通過在函數組件里調用它來給組件添加一些內部state。

React會在重復渲染時保留這個state

useState會返回一對值:當前狀態和一個讓你更新它的函數,你可以在事件處理函數中或其他一些地方調用這個函數。它類似class組件的this.setState,但是它不會把新的state和舊的state合並

import React, { useState } from "react";
function App() {
  const [obj, setObject] = useState({
    count: 0,
    name: "alife"
  });
  return (
    <div className="App">
      Count: {obj.count}
      <button onClick={() => setObject({ ...obj, count: obj.count + 1 })}>+</button>
      <button onClick={() => setObject({ ...obj, count: obj.count - 1 })}>-</button>
    </div>
  );
}

2、useEffect

useEffect用來幫助開發者處理函數的副作用

import React, { useState, useEffect } from "react";
let timer = null;
function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = "componentDidMount" + count;
  },[count]);
 
  useEffect(() => {
    timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
    // 一定注意下這個順序:
    // 告訴react在下次重新渲染組件之后,同時是下次執行上面setInterval之前調用
    return () => {
      document.title = "componentWillUnmount";
      clearInterval(timer);
    };
  }, []);
  return (
    <div>
      Count: {count}
      <button onClick={() => clearInterval(timer)}>clear</button>
    </div>
  );
}

useEffect 第一個參數接收一個函數,可以用來做一些副作用比如異步請求,修改外部參數等行為,而第二個參數稱之為dependencies,是一個數組,如果數組中的值變化才會觸發 執行useEffect 第一個參數中的函數。返回值(如果有)則在組件銷毀或者調用函數前調用

  • 1.比如第一個 useEffect 中,理解起來就是一旦 count 值發生改變,則修改 documen.title 值;
  • 2.而第二個 useEffect 中傳遞了一個空數組[],這種情況下只有在組件初始化或銷毀的時候才會觸發,用來代替 componentDidMount 和 componentWillUnmount,慎用;
  • 3.還有另外一個情況,就是不傳遞第二個參數,也就是useEffect只接收了第一個函數參數,代表不監聽任何參數變化。每次渲染DOM之后,都會執行useEffect中的函數。

基於useEffect封裝的生命周期函數(componentDidUpdate)

function useUpdate(fn) {
    // useRef 創建一個引用
    const mounting = useRef(true);
    useEffect(() => {
      if (mounting.current) {
        mounting.current = false;
      } else {
        fn();
      }
    });
}

 

3、useContext

useContext是用來處理多層級傳遞數據的方式。

const colorContext = React.createContext("gray");
function Bar() {
  const color = useContext(colorContext);
  return <div>{color}</div>;
}
function Foo() {
  return <Bar />;
}
function App() {
  return (
    <colorContext.Provider value={"red"}>
      <Foo />
    </colorContext.Provider>
  );
}

使用context解決了Provider和Consumer的多層嵌套的問題

4、useReducer

useReducer這個hooks在使用上幾乎跟Redux/React-Redux一模一樣,唯一缺少的就是無法使用redux提供的中間件

而且跟Redux基本上是一致的,用法也很簡單,算是提供一個mini的Redux版本

import React, { useReducer } from "react";
const initialState = {
  count: 0
};
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + action.payload };
    case "decrement":
      return { count: state.count - action.payload };
    default:
      throw new Error();
  }
}
function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "increment", payload: 5 })}>
        +
      </button>
      <button onClick={() => dispatch({ type: "decrement", payload: 5 })}>
        -
      </button>
    </>
  );
}

 

4、useCallback

function App() {
  const memoizedHandleClick = useCallback(() => {
    console.log('Click happened')
  }, []); // 空數組代表無論什么情況下該函數都不會發生改變
  return <SomeComponent onClick={memoizedHandleClick}>Click Me</SomeComponent>;
}

第二個參數傳入一個數組,數組中的每一項一旦值或者引用發生改變,useCallback就會重新返回一個新的記憶函數提供給后面渲染

 

5、useMemo

useMemo的功能和useCallback是相似的,如果你想通過使用useMemo返回一個記憶函數也是完全可以的

可以改寫用useCallback寫過的例子:

function App() {
  const memoizedHandleClick = useMemo(() => () => {
    console.log('Click happened')
  }, []); // 空數組代表無論什么情況下該函數都不會發生改變
  return <SomeComponent onClick={memoizedHandleClick}>Click Me</SomeComponent>;
}

唯一的區別是:useCallback不會執行第一個參數函數,而是將它返回給你,而useMemo會執行第一個函數並且將執行結果返回給你,所以在前面的例子中,可以返回handleClick來達到存儲函數的目的

所以useCallback常用記憶事件函數,生成記憶后的事件函數並傳遞給子組件使用。而useMemo適合經過函數計算得到一個確定的值,比如記憶組件

 

6、useRef

useRef跟createRef類似,都可以用來生成DOM對象

import React, { useState, useRef } from "react";
function App() {
  let [name, setName] = useState("Nate");
  let nameRef = useRef();
  const submitButton = () => {
    setName(nameRef.current.value);
  };
  return (
    <div className="App">
      <p>{name}</p>
 
      <div>
        <input ref={nameRef} type="text" />
        <button type="button" onClick={submitButton}>
          Submit
        </button>
      </div>
    </div>
  );
}

useRef返回的值傳遞給組件或者DOM的ref屬性,就可以通過ref.current值訪問組件或真實的DOM節點,重點是組件也可以訪問到的,從而可以對DOM進行一些操作,比如監聽事件等等

當然useRef遠比你想象中的功能更加強大,useRef的功能有點像類屬性,或者說您想要在組件中記錄一些值,並且這些值在稍后可以更改

function App() {
  const [count, setCount] = useState(0);
 
  useEffect(() => {
    setTimeout(() => {
      alert("count: " + count);
    }, 3000);
  }, [count]);
 
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>增加 count</button>
      <button onClick={() => setCount(count - 1)}>減少 count</button>
    </div>
  );
}

先點擊增加button,后點擊減少button,3秒后先alert 1,后alert 0,而不是alert兩次0。這就是所謂的 capture value 的特性。而在類組件中 3 秒后輸出的就是修改后的值,因為這時候** message 是掛載在 this 變量上,它保留的是一個引用值**,對 this 屬性的訪問都會獲取到最新的值,類組件舉例,在線Demo。講到這里你應該就明白了,useRef 創建一個引用,就可以有效規避 React Hooks 中 Capture Value 特性。useRef避免 Capture Value 

function App() {
  const count = useRef(0);
 
  const showCount = () => {
    alert("count: " + count.current);
  };
 
  const handleClick = number => {
    count.current = count.current + number;
    setTimeout(showCount, 3000);
  };
 
  return (
    <div>
      <p>You clicked {count.current} times</p>
      <button onClick={() => handleClick(1)}>增加 count</button>
      <button onClick={() => handleClick(-1)}>減少 count</button>
    </div>
  );
}

只要將賦值與取值的對象變成 useRef,而不是 useState,就可以躲過 capture value 特性,在 3 秒后得到最新的值。

 

7、useImperativeHandle

通過 useImperativeHandle 用於讓父組件獲取子組件內的索引

import React, { useRef, useEffect, useImperativeHandle, forwardRef } from "react";
function ChildInputComponent(props, ref) {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => inputRef.current);
  return <input type="text" name="child input" ref={inputRef} />;
}
const ChildInput = forwardRef(ChildInputComponent);
function App() {
  const inputRef = useRef(null);
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return (
    <div>
      <ChildInput ref={inputRef} />
    </div>
  );
}

8、useLayoutEffect

使用 useEffect 就可以幫我們處理組件的副作用,但是如果想要同步調用一些副作用,比如對 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用會在 DOM 更新之后同步執行。

function App() {
  const [width, setWidth] = useState(0);
  useLayoutEffect(() => {
    const title = document.querySelector("#title");
    const titleWidth = title.getBoundingClientRect().width;
    console.log("useLayoutEffect");
    if (width !== titleWidth) {
      setWidth(titleWidth);
    }
  });
  useEffect(() => {
    console.log("useEffect");
  });
  return (
    <div>
      <h1 id="title">hello</h1>
      <h2>{width}</h2>
    </div>
  );
}

useEffect和useLayoutEffect有什么區別?

簡單來說就是調用時機不同,useLayoutEffect和原來componentDidMount&componentDidUpdate一致,在react完成DOM更新后馬上同步調用的代碼,會阻塞頁面渲染。而useEffect是會在整個頁面渲染完才會調用的代碼。

在實際使用時如果想避免頁面抖動(在useEffect里修改DOM很有可能出現)的話,可以把需要操作DOM的代碼放在useLayoutEffect里。關於使用useEffect導致頁面抖動,參考git倉庫git倉庫示例

不過useLayoutEffect在服務端渲染時會出現一個warning,要消除的話得用useEffect代替或者推遲渲染時機。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM