赞
踩
useState简介
useState的简单使用
手写实现useState
总结
参考
返回一个有状态值和一个函数来更新它。在初始渲染期间,返回的状态(状态)与作为第一个参数(initialState)传递的值相同。setState 函数用于更新状态。它接受一个新的状态值,并排队等待重新渲染该组件。
在后续重新渲染期间,useState 返回的第一个值将始终是应用更新后的最新状态。
注意:
React 保证 setState 函数身份是稳定的,并且在重新渲染时不会改变。这就是为什么可以安全地从 useEffect 或 useCallback 依赖项列表中省略的原因。
useState 相当于我们在原生 JS 中定义变量。而只有使用这种方法定义变量,React 才能知道相应的变量。
注意
:在组件的定义中,首字母必须大写。
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> ); } }
const Functions = () => {
const [n, setN] = React.useState(0);
return (
<div className="grandson">
函数组件--n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
使用 ReactDom.render()渲染 App。
function App() {
return (
<div className="app">
useState的简单使用
<Classes />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
useState 有两个状态,一个是 n,另外一个是 setN。
setN 是修改数据 n 的,将修改后的 n 存入 state。
setN 修改数据后一定会触发<App/>的重新渲染(re-render)。
useState 一定会从 state 读取最新的 n 值。
每个组件都有自己的数据 state。
第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的实现使用了链表。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。