2

引入

当你看到这样一串代码,你是怎么想的?

function  App()  {
    const  [n, setN] =  React.useState(0);
    return  (
        <div  className="App">
            <p>{n}</p>
            <button  onClick={() =>  setN(n + 1)}>+1</button>
        </div>
    );
}
ReactDOM.render(<App  />,  rootElement);

我来猜一猜:

  1. 用useState声明了一个n,一个改变n值的方法setN,让n初始值为0
  2. 调用setN之后n的值会发生改变

但过程真的是这样的吗?

console.log

  1. 在App组件内加一句console.log('App');
    你会发现App在每一次点击button,都进行了刷新
    每次setN都重新进行了render
  2. 在App组件内加一句console.log(n)
    你会发现每次点击button,打出的n都是不同的,第一次是0,第二次是1,第三次是2 ...
    useState(0)中的0只在第一次初始化生效(否则每一次执行n都是0)

雏形

function  myUseState(initialValue)  {
    var  state  =  initialValue;
    function  setState(newState)  {
        state  =  newState;
        render();
    }
    return  [state,  setState];
}
const render = () => ReactDOM.render(<App  />, rootElement);

点击后发现了什么呢? n的值并没有改变。
因为每一次myUseState都把n初始化成了0

改良

let myState
function  myUseState(initialValue)  {
    var  myState  = myState===undefined? initialValue : myState;    //控制初始化
    function  setState(newState)  {
        myState  =  newState;
        render();
    }
    return  [myState,  setState];
}
const render = () => ReactDOM.render(<App  />, rootElement);
  • 经过改造,myUseState终于正常运行了
  • 你也可以看到,setN根本没有改变n,它改变了一个myState的值,保存在其他地方
  • 在调用setN的时候,实际上是把myState的值赋给n,并且重新进行了rerender,才给你造成了setN改变了n的错觉

我用了好多个useState咋办?

方案 1 : 把myState做成对象 {n:0, m:1}

  • useState()只能传一个参数,没法区分键名n和m

方案 2 : 把myState做成数组 [0, 1]

  • 可行(就是这种思路实现的)

但是!方案 2 会区分顺序!!
所以还需要index来辅助记录顺序
这也就导致了我们在使用useState的时候必须写在顶层,不能放在判断语句中,否则就会打乱数组的顺序造成错误。

我在好多个组件都用了useState咋办?

方案1:放在全局变量里

  • 只有智障才会放在全局变量里

方案2:放在虚拟React节点中

  • 虚拟react节点的数据类型是对象,正好可以在每一个组件的节点上加上 myState 和 index 的键值对!完美解决

Oliver
76 声望13 粉丝

Slow Done, Achieve More.