关于 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 等对象

相关文章
相关标签/搜索