【React】Hooks全解之逐個擊破

Ⅰ. useState

在React函數組件中,是默認只有屬性沒有狀態的,咱們能夠使用以下代碼用useState讀寫變量和設定初始值 :react

const [n,setN] = react.useState(0)redux

const [user,setUser] = react.useState({name:'Jack'})數組

💡 注意事項bash

  1. state不能夠局部更新

咱們沒法局部setState,由於react函數組件不會幫咱們合併屬性。解決辦法是使用react提供的...語法將原屬性拷貝並放在更新值以前。ide

  1. set的對象必須是新對象

由於setState(obj)obj若是是在原來的對象上進行修改,那麼對象的地址沒有更改,react會將這個對象斷定爲原對象,則更改無效。解決方法是始終設置新的對象就能夠了。函數

  1. useState接受函數,setState優先使用函數

① useState的初始值並不侷限於對象,能夠用函數的形式設定。佈局

const [state, setState] = useState(()=>{ return initialState })ui

② 因爲表達式的侷限性,若是你須要在setState中進行屢次操做,那麼須要使用函數的形式以確保每一次的操做是有效的。spa

const onClick = ()=>{
    setN(n+1) //無效操做
    setN(n+1) //你會發現 n 不能加 2
  }
複製代碼
const onClick = ()=>{
    setN(i=>i+1) //有效操做
    setN(i=>i+1) //最後值爲n+2
  }
複製代碼

Ⅱ.useReducer

若是你發現有幾個變量須要彙總在一塊兒,你能夠使用複雜版的useState —— useReducer,它的好處就是能夠對這個對象進行一個總體的操做 。而且能夠踐行React社區一直推崇的Flux/Redux思想,在我看來這個思想可能會在不久的未來Hooks思想盛行以後逐漸被取代,可是目前仍是須要了解這個被普遍應用的Hook。翻譯

使用useReducer分爲四個步驟:

  • 建立初始值initialState
  • 建立全部操做reducer(state,action)
  • 傳給useReducer獲得讀和寫API
  • 調用寫({type:"操做類型"})

一個🌰:

const initial = {
    n:0
};

const reducer = (state,action) => {
    if(action.type === "add") {
        return { n:state.n + 1 };
    } else if (action.type === "multi") {
        return { n:state.n * 2 };
    } else {
        throw new Error("unknown type");
    }
  };
  function App() {
      const [state, dispatch] = useReducer(reducer, initial);
      const { n } = state;
      const onClick = () => {
          dispatch({ type: "add", number: 1 });
      };
      const onClick2 = () => {
          dispatch({ type: "multi", number: 2 });
      };
      return (
      <div className = "App">
        <div>n: {n}</div>
        <button onClick={onClick}>+1</button>
        <button onClick={onClick2}>*2</button>
      </div>    
    );
}
複製代碼

如何代替redux?

  1. 將數據集中在一個store對象;
  2. 將全部操做集中在reducer;
  3. 建立一個Context;
  4. 建立對數據讀寫的API;
  5. 將第四步的操做放進Context中;
  6. 用Context.Provider將Context提供給i全部組件;
  7. 各個組件用useContext獲取讀寫API。

Ⅲ.useContext

這個Hook翻譯過來就是「上下文」。何爲上下文?上下文就是你在運行一個程序的時候所要知道的全部變量。全局變量就是全局的上下文,上下文是局部的全局變量。上一個Hook中咱們已經瞭解到useContext的用法。

一個🌰:

const C = createContext(null);

function App() {
  const [n, setN] = useState(0);
  return (
    <C.Provider value={{ n, setN }}>
      <div className="App">
        <Dog />
      </div>
    </C.Provider>
  );
}

function Dog() {
  const { n, setN } = useContext(C);
  return (
    <div>
      狗 n: {n} <Child />
    </div>
  );
}

function Child() {
  const { n, setN } = useContext(C);
  const onClick = () => {
    setN(i => i + 1);
  };
  return (
    <div>
      狗兒子 n: {n}
      <button onClick={onClick}>+1</button>
    </div>
  );
}
複製代碼

Ⅳ.useEffect

這個Hook翻譯過來爲「反作用」,我認爲把它叫作afterRender更好,由於這是每次render以後就會調用的一個函數。 用途:

  • 做爲componentDidMount(出生後)使用

    useEffect(()=>{console.log('hi'),[]}) //使用空數組表示第一次渲染時

  • 做爲componentUpdate(更新)使用

    useEffect(()=>{console.log('hi'),[n]}) //表示當{n}變化時

    useEffect(()=>{console.log('hi')}) //表示每一次更新時

  • 做爲componentWillUnmount(將死)使用

    useEffect(()=>{console.log('hi')})
    return ()=>{} //添加return表示當組件將時執行某操做
    複製代碼

useLayoutEffect

  • 佈局反作用:useEffect在渲染後執行,useLayoutEffect在渲染前執行。
  • 特色: useLayoutEffect老是比useEffect先執行。
  • 經驗: 爲了用戶體驗,優先使用useEffect(優先渲染)。

Ⅴ.useMemo & useCallback

因爲react默認會有多餘的render出現,若是props不變就沒要再次執行這個函數組件,useMemo就是阻止函數組件無效執行的Hook。

❗❗ 若是在函數組件中添加一個監聽事件,則useMemo會直接無效,須要寫成:

useMemo(()=> (x) => console.log(x))這種形式,一個返回函數的函數。 因爲這種方式難用且難以理解,因而誕生了useCallback

useCallback(x => log(x), [m]) //等價於下面一種寫法
useMemo(() => x => log(x), [m])
複製代碼

Ⅵ.useRef

若是你須要一個值在組件不斷render的時候保持始終是同一個值地址不變,那就須要用到useRef。

首先須要初始化:const n = useRef(0)

讀取時則是:n.current (這裏的current是用來保證兩次useRef的是相同的值的引用)

❗❗ useRef是不能作到變化的時候自動render的,由於這不符合react的理念,若是你想要這個功能,徹底能夠本身加,監聽ref,當ref.current變化的時候,調用setX便可。

Ⅶ.自定義Hook

自定義Hook必須以use開頭,這是react既定的語法。經過自定義 Hook,能夠將組件邏輯提取到可重用的函數中。

一個🌰:

function App() {
  const { list } = useList();
  return (
    <div className="App">
      <h1>List</h1>
      {list ? (
        <ol>
          {list.map(item => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ol>
      ) : (
        "加載中..."
      )}
    </div>
  );
}
複製代碼

與 React 組件不一樣的是,自定義 Hook 不須要具備特殊的標識。咱們能夠自由的決定它的參數是什麼,以及它應該返回什麼(若是須要的話)。換句話說,它就像一個正常的函數,咱們能夠最大限度的靈活使用這個Hook。

相關文章
相關標籤/搜索