目录
useState简介
useState的简单使用
手写实现useState
总结
参考
一、useState 简介
返回一个有状态值和一个函数来更新它。在初始渲染期间,返回的状态(状态)与作为第一个参数(initialState)传递的值相同。setState 函数用于更新状态。它接受一个新的状态值,并排队等待重新渲染该组件。
在后续重新渲染期间,useState 返回的第一个值将始终是应用更新后的最新状态。
注意:
React 保证 setState 函数身份是稳定的,并且在重新渲染时不会改变。这就是为什么可以安全地从 useEffect 或 useCallback 依赖项列表中省略的原因。
useState 相当于我们在原生 JS 中定义变量。而只有使用这种方法定义变量,React 才能知道相应的变量。
二、useState 的简单使用
注意
:在组件的定义中,首字母必须大写。
1、在类组件中使用 useState
class Classes extends React.Component {
constructor(props) {
super(props);
this.state = {
n: 0,
};
}
add() {
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div className="son">
类组件--n:{this.state.n}
<button onClick={() => this.add()}>+1</button>
<Functions />
</div>
);
}
}
2、在函数组件中使用 useState
const Functions = () => {
const [n, setN] = React.useState(0);
return (
<div className="grandson">
函数组件--n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
3、App 组件
使用 ReactDom.render()渲染 App。
function App() {
return (
<div className="app">
useState的简单使用
<Classes />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
4、最终效果呈现
三、手写实现 useState
1、分析
-
useState 有两个状态,一个是 n,另外一个是 setN。
-
setN 是修改数据 n 的,将修改后的 n 存入 state。
-
setN 修改数据后一定会触发<App/>的重新渲染(re-render)。
-
useState 一定会从 state 读取最新的 n 值。
-
每个组件都有自己的数据 state。
2、尝试实现 React.useState
第1次写myUseState
function myUseState(initialValue) {
let state = initialValue;
const setState = (newValue) => {
state = newValue; //更新state值,
render(); //触发重新渲染
};
return [state, setState];
}
/* 粗糙的渲染 */
const render = () => {
ReactDOM.render(<App />, document.getElementById("root"));
};
// 使用myUseState
const App = () => {
const [n, setN] = myUseState(0);
return (
<div classNam="App">
<p>n:{n}</p>
<button onClick={()=>{setN(n+1)}}>n+1</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
第一次总结
运行第一次写的myUseState时发现,页面完全没有变化。n值也没有发生改变。经分析,是因为每次调用myUseState时会重置state的值
。经过改进,必须将state写在函数的外面
。
第2次写myUseState
let _state;
function myUseState(initialValue) {
_state = _state===undefined? initialValue:_state;
const setState = (newValue) => {
_state = newValue; //更新state值,
render(); //触发重新渲染
};
return [_state, setState];
}
/* 粗糙的渲染 */
const render = () => {
ReactDOM.render(<App />, document.getElementById("root"));
};
// 使用myUseState
const App = () => {
const [n, setN] = myUseState(0);
return (
<div classNam="App">
<p>n:{n}</p>
<button onClick={()=>{setN(n+1)}}>n+1</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
第二次总结
运行成功,页面中的n发生了变化!
但是问题又来了,如果一个组件用了两个useState
怎么办?_state的值不是会发生冲突吗?继续改进完善myUseState。将_state做成数组
。
第3次写myUseState
let _state=[];
let index=0;
function myUseState(initialValue) {
int currentIndex=index; //引入中间变量currentIndex就是为了保存当前操作的下标index。
_state[currentIndex] = _state[currentIndex]===undefined? initialValue:_state[currentIndex];
const setState = (newValue) => {
_state[currentIndex] = newValue;
render();
};
index+=1;// 每次更新完state值后,index值+1
return [_state[currentIndex], setState];
}
const render = () => {
index=0; //重要的一步,必须在渲染前后将index值重置为0,不然index会一种增加1
ReactDOM.render(<App />, document.getElementById("root"));
};
// 使用myUseState
const App = () => {
const [n, setN] = myUseState(0);
const [m, setM] = myUseState(0);
return (
<div classNam="App">
<p>n:{n}</p>
<button onClick={()=>{setN(n+1)}}>n+1</button>
<p>m:{m}</p>
<button onClick={()=>{setM(m+1)}}>n+1</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
第三次总结
运行成功!n,m值都能单独的发生变化。
四、总结
本次我只是简单分析了React实现useState的思想和代码。至于React真正实现useState的方式肯定是比我手写的myUseState更加高级和高效的。
useState调用顺序
若第一次渲染的时候,n是第一个数据,m是第二个数据,k是第三个。…
则第二次渲染的时候必须保证完全的一致。
React不允许出现以下类似的代码,否则会出现报错信息:React Hook "React.useState" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
译:有条件地调用React Hook“ React.useState”。在每个组件中,必须以完全相同的顺序调用React Hooks来渲染react-hooks / rules-of-hooks
let n=2;
if(n%2){
const [m,setM]=React.useState(0);
}
/* 以上代码React会直接报错 */
每个组件命名的问题
App用了_state和index,那其他组件用什么?放在全局作用域重名了怎么办?
解决办法1:每个组件都创建一个_state和index。
解决办法2:放在组件对应的虚拟节点对象上
注意
:
React的节点应该是FiberNode,_state的真实名称为memorizedState,index的实现使用了链表。