關於 React Hooks 的 useState 的原理解析

一、關於 useState

  • 在學習 React 的 useState 時,確定會遇到一個疑問,useState 到底作了什麼,能夠得到一個既能夠讀,又能夠寫入的數組呢?
  • 執行 setN 的時候會發生什麼?n 會變嗎?App() 會從新執行嗎?
  • 若是 App() 會從新執行,那麼 useState(0) 的時候,每次的值會有不一樣嗎?
function App() {
  const [n, setN] = useState(0);
  const [m, setM] = useState(0);
  return (
    <div className="App">
      <p>{n}</p>
      <p>
        <button onClick={() => setN(n + 1)}>+1</button>
      </p>
      <p>{m}</p>
      <p>
        <button onClick={() => setM(m + 1)}>+1</button>
      </p>
    </div>
  );
}
複製代碼

分析:react

  • setN
    • setN 必定會修改某個數據 x ,將 n+1 存入這個 x 中
    • setN 必定會觸發 從新渲染
  • useState
    • useState 確定會從 x 讀取 n 的最新值
  • x
    • 每一個組件都有本身的數據 x,咱們將其命名爲 state

二、手寫一個簡易版 useState 的代替品 useMyState

import React from "react";
import ReactDOM from "react-dom";
const rootElement = document.getElementById("root");
let _state;
const myUseState = (initState) => {
    _state = ( _state === undefined ? initState : _state);
    const setState = (newState) => {
        _state = newState
        render()
    }
    return [_state,setState]
}
const render = () => ReactDOM.render(<App />, rootElement);

function App() {
    const [n, setN] = myUseState(0);
    return (
        <div className="App">
            <p>{n}</p>
            <p>
                <button onClick={() => setN(n + 1)}>+1</button>
            </p>
        </div>
    );
}
ReactDOM.render(<App />, rootElement);
複製代碼
  • 可是這樣的代碼也會遇到問題,若是除了 n 還有個 m 呢?

三、useMyState 方法進階版

import React from "react";
import ReactDOM from "react-dom";
const rootElement = document.getElementById("root");

let _state = [];
let index = 0;
const myUseState = (initState) => {
  let currentIndex = index;
  _state[currentIndex] = ( _state[currentIndex] === undefined ? initState : _state[currentIndex]);
  const setState = (newState) => {
    _state[currentIndex] = newState
    index = 0
    render()
  }
  index += 1
  return [_state[currentIndex],setState]
}
const render = () => ReactDOM.render(<App />, rootElement);

function App() {
  const [n, setN] = myUseState(0);
  const [m, setM] = myUseState(0);
  return (
    <div className="App">
      <p>{n}</p>
      <p>
        <button onClick={() => setN(n + 1)}>+1</button>
      </p>
      <p>{m}</p>
      <p>
        <button onClick={() => setM(m + 1)}>+1</button>
      </p>
    </div>
  );
}
複製代碼
  • 使用數組方法的缺點:
    • useState 的調用順序很是重要
    • 第一次渲染時 n 在第一個,m 第二個,k 第三個
    • 那麼第二次渲染時的調用順序必須徹底一致
    • 所以,React 不容許出現 if...else..
    • 示例:
const [n, setN] = React.useState(0);
let m, setM;
if (n % 2 === 1) {
    [m, setM] = React.useState(0);
}
複製代碼

四、useState 的 n

function App() {
    const [n, setN] = React.useState(0);
    const log = () => setTimeout(() => console.log(`n: ${n}`), 3000);
    return (
        <div className="App">
            <p>{n}</p>
            <p>
                <button onClick={() => setN(n + 1)}>+1</button>
                <button onClick={log}>log</button>
        </p>
        </div>
    );
}

//先點擊 +1,再點 loglog 的值爲 1
//先點擊 log,再點 +1,log 的值爲 0
複製代碼
  • 由於每一次 useState 渲染出 App() 裏的 n 都是一個新的 n,也就是說 n=0 和 n=1,是並存的,點擊 log 後,log 出的是原來等於 0 的 n,而不是 +1 後生成的新的 n、

五、useRef

function App() {
    const nRef = React.useRef(0); 
    //nRef 是一個對象 { current:0 }
    const log = () => setTimeout(() => console.log(`n: ${nRef.current}`), 1000);
    return (
        <div className="App">
            <p>{nRef.current}</p>
            <p>
                <button onClick={() => (nRef.current += 1)}>+1</button>
                <button onClick={log}>log</button>
            </p>
        </div>
    );
}
複製代碼
  • 這樣就能夠解決上面 n 的分身的問題了,可是又會出現新的問題:UI 並不會進行實時的更新,也就是說 useRef 並不會觸發渲染
  • 解決方法:添加使用 useState 來觸發更新(不推薦)
  • 示例:
const nRef = React.useRef(0);
const update = React.useState()[1];
const log = () => setTimeout(() => console.log(`n: ${nRef.current}`), 1000);
return (
    <div className="App">
        <p>{nRef.current} 這裏並不能實時更新</p>
        <p>
            <button onClick={() => ((nRef.current += 1), update(nRef.current))}>
+1
            </button>
            <button onClick={log}>log</button>
        </p>
    </div>
);
複製代碼

六、總結

  • 每一個函數組件對應一個 React 節點數組

  • 每一個節點保存着 state 和 indexbash

  • useState 會讀取 state[index]dom

  • index 由 useState 出現的順序決定函數

  • setState 會修改 state ,並觸發更新學習

  • 每次渲染,組件函數就會再次執行ui

  • 對應的全部 state 都會出現一個「分身」spa

  • 若是不但願出現這樣「分身」的狀況code

  • 可使用 useRef / useContext 等對象

相關文章
相關標籤/搜索