useState原理

简介: 每个函数组件对应一个React节点每个节点保存着state和index (这个state可能描述不准确,但主要是让大家想清楚这个事情,应该是一个类似state的东西)useState会读取state[index]index由useState出现的顺序决定setState会修改State,并触发更新

useState用法

脑补一下点击button后会发生什么

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

会使页面上的n变量由0变成1

71604b3e9f920719707d9a6efc82bc1.png

脑补之后

问自己几个问题

执行setN的时候会发生什么?n会变吗?App()会重新执行吗?

如果App()会重新执行,那么useState(0)的时候,n每次的值会有不同吗?

通过console.log你就能得到答案

分析

setN

setN一定会修改数据x,(我们暂且将这个不知道的东西称为x),将n+1存入x

setN一定会触发<App/>重新渲染(render)

useState

useState肯定会从x读取到n的最新值

x

每个组件有自己的数据x,我们将其命名为state

尝试实现React.useState

function myUseState(initialValue) {
  var state = initialValue;
  function setState(newState) {
    state = newState;
    render();
  }
  return [state, setState];
}
// render方法我们就不实现了,直接让它渲染
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>
  );
}

完全没有变化啊

因为myUseState会将state重置

我们需要一个不会被myUseState重置的变量

那么这个变量只要声明在myUseState外面即可

// ***********************************
let _state;
// ***********************************
function myUseState(initialValue) {
  _state = _state === undefined ? initialValue : _state;
  function 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>
  );
}

useState就这么简单?

别急,还有问题

如果一个组件用了两个useState怎么办?

由于所有数据都方在_state,所以会冲突

改进思路

把_state做成一个对象

比如_state = { n:0, m:0}

不行,因为useState(0)并不知道变量叫n还是m

把_state做成数组

比如_state= [ 0, 0 ]

貌似可行,我们来试试看

多个useState

// ***********************************
let _state = [];
let index = 0;
// ***********************************
function myUseState(initialValue) {
  const currentIndex = index;
  index += 1;
  _state[currentIndex] = _state[currentIndex] || initialValue;
  const setState = newState => {
    _state[currentIndex] = newState;
    render();
  };
  return [_state[currentIndex], setState];
}
const render = () => {
  index = 0;
  ReactDOM.render(<App />, rootElement);
};
function App() {
  const [n, setN] = myUseState(0);
  const [m, setM] = myUseState(0);
  console.log(_state);
  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>
  );
}

_state数组方案缺点

useState调用顺序

如果第一次渲染时,n是第一个,m是第二个,k是第三个

则第二次渲染时必须保证顺序完全一致

所以React不允许出现如下代码

2 ReactDOM . render (< cApp />, rootElement ):
25
 Function App (){
 const [ n , setN ] React . useState (0); let m , setM ;
 if ( n %2-1)
[ m , setM ]= React . useState (0): return (
 cdiv className -" App ">< p > fnj </ p >
< button onC1ick={()=> setN ( n .1))>+1</ button >
Console wos cleored 
 Uncaught Invariant Violation : Rendered more hooks than during the previous render .
 invariant (https://j6y72.csb.anp/no
 odules / react - dom / cjs / react - dom . develonment . js :55:15)
 updateworkinProgressHook (httR5i/Z1by72.csb. anp / node moduies / react - gon /c3sreact- gom . oeveopn 
 updateReducer (https://16y72.csb.app/nodem
modu1es/ react - dom /c1s/ react - dom .deye1ooment.1s1139991
 nt .1s:13294:10)

总结

  1. 每个函数组件对应一个React节点
  2. 每个节点保存着stateindex (这个state可能描述不准确,但主要是让大家想清楚这个事情,应该是一个类似state的东西)
  3. useState会读取state[index]
  4. indexuseState出现的顺序决定
  5. setState会修改State,并触发更新



目录
相关文章
|
存储 编解码 并行计算
【软件设计师备考 专题 】计算机系统的组成、体系结构分类及特性
【软件设计师备考 专题 】计算机系统的组成、体系结构分类及特性
298 0
|
Linux Docker 异构计算
ModelScope问题之下载了官方镜像 但是启动不了如何解决
ModelScope镜像是指用于在ModelScope平台上创建和管理的容器镜像,这些镜像包含用于模型训练和推理的环境和依赖;本合集将说明如何使用ModelScope镜像以及管理镜像的技巧和注意事项。
510 0
|
9月前
|
存储 运维 监控
阿里云飞天洛神云网络子系统“齐天”:超大规模云网络智能运维的“定海神针”
阿里云飞天洛神云网络子系统“齐天”:超大规模云网络智能运维的“定海神针”
341 3
|
SQL 存储 关系型数据库
MySQL能否查询某张表的操作记录
MySQL能否查询某张表的操作记录
2275 1
|
Web App开发 监控 Java
Electron V8排查问题之发现的内存泄漏问题如何解决
Electron V8排查问题之发现的内存泄漏问题如何解决
533 0
|
小程序
微信小程序App()方法与getApp()方法
微信小程序App()方法与getApp()方法
930 0
微信小程序App()方法与getApp()方法
|
Web App开发 Linux iOS开发
CrossOver 2022无需虚拟机即可运行win系统exe文件
今晚有一个学习群的同学在mac上安装了一款只适用于win的软件。安装方法:先安装crossover这款软件。(本人没用过苹果笔记本,对苹果笔记本完全不了解的状态,如果mac需要安装一些win软件的话,可以自己了解一下)
588 0
|
机器学习/深度学习 数据采集 人工智能
自动化机器学习(AutoML)入门简介
近期在学习研究一些关于自动化机器学习方面的论文,本文作为该系列的第一篇文章,就AutoML的一些基本概念和现状进行简单分享,权当抱砖引玉。
734 0
自动化机器学习(AutoML)入门简介
初露锋芒 Angular6 入门项目 - 4 下
初露锋芒 Angular6 入门项目 - 4 下
193 0
|
JavaScript 前端开发 索引
ES6 系列之迭代器与 for of
看着很简单,但是再回顾这段代码,实际上我们仅仅是需要数组中元素的值,但是却需要提前获取数组长度,声明索引变量等,尤其当多个循环嵌套的时候,更需要使用多个索引变量,代码的复杂度就会大大增加,比如我们使用双重循环进行去重。
197 0
ES6 系列之迭代器与 for of