(一)useState
最近在看react的新官网,对react的一些用法查缺补漏,顺便记录一下,也方便之后查看,本文主要讲解一下useState的一些用法和注意事项,一起学习,如有独特的见解,欢迎评论区讨论!
说明
一个常用的React Hooks,它可以给一个函数式的组件添加一个状态变量。
const [state, setState] = useState(initialState);
组成
-
参数 - initialState
- 初始化的值,可以是任何类型;
- 可以传入一个纯函数,不接收任何参数,返回一个值(可以是任何类型),这个函数会被当做初始化的函数,作为 useState 的初始值;
- 这个值只有在初次渲染的时候使用,所以传入函数引用即可,不要已执行的方式传入,因为这个值只会在初始化的时候使用,传入函数执行,每次渲染都会执行函数,又不会使用它,造成性能浪费;
- 传入的是引用值,比如对象或者数组,这个引用值对于 react 可以理解为 readonly,如果需要改变,请替换整个引用,比如可以使用展开运算符。
-
returns - 返回两个值
- 第一个值为当前的状态,第一次渲染为传入的 initialState;
- 第二个值为一个 set 函数,作用是更新状态的值,触发页面重新渲染;
- 直接传入下一个状态的值;
- 传入一个函数,从上一个状态计算当前状态,函数必须为纯函数,接收一个state作为唯一的参数;
- 如果set函数提供的新值与旧值相同,React将不会重新渲染组件;
- set函数没有返回值;
function Test() {
const [count, setCount] = useState(0)
const handleClick () => {
setCount(count + 1) // 直接更新
setCount(state => state + 1) // 函数更新
}
}
用法
直接更新和函数更新的区别
- 直接更新,在一次渲染中,无论调用多少次set函数,页面只会更新1次,相同的set函数只有最后一次调用的会生效;
- 通过函数改变状态,页面也只会渲染一次,但是每一次调用都能够获取到上一次的状态进行计算,react会将函数放入到一个队列当中,在渲染前会依次执行函数;
const [number, setNumber] = useState < number > 0;
const [age, setAge] = useState < number > 0;
/** 直接改变状态 - number = 1 */
const increaseNumber = () => {
// 页面渲染一次,累加1次,最后一次有效
setNumber(number + 1);
setNumber(number + 2);
setNumber(number + 1);
};
/** 函数改变状态 number = 3 */
const increaseAge = () => {
// 页面渲染一次,但是会累加3次,每一次都有效
setAge((state) => state + 1);
setAge((state) => state + 1);
setAge((state) => state + 1);
};
return (
<div>
<button onClick={increaseNumber}>点击改变number:{number}</button>
<button onClick={increaseAge}>点击改变age:{age}</button>
</div>
);
使用key属性重置子组件的状态
当我们在渲染一个list列表的时候会经常使用key属性,方便React优化更新,在这里讲解的是key的另一种用法.
我们可以给组件传递一个key属性,当属性值发生变化的时候,这个组件将会被重置
function App() {
const [version, setVersion] = useState(0);
const handleClick = () => setVersion(version + 1);
return (
<>
<button onClick={handleClick}>Reset</button>
<Form key={version} />
</>
);
}
function Form() {
const [rule, setRule] = useState('默认规则');
return (
<>
<input value={rule} onChange={(e) => setRule(e.target.value)} />
<p>rule is: {rule}</p>
</>
);
}
注意事项
- 只能在函数式组件的顶层作用域中或者自定义Hooks的顶层作用域中使用他,不能写在循环语句或者条件语句中。
- 在开发环境中,如果开启了严格模式,React会调用两次初始化函数,目的是为了帮我们一些可能产生意外的代码,如果你的初始化函数不是一个纯函数就可能会出现问题。
常见问题
避免重复创建初始化状态
react只会在第一次render的时候使用这个initState,在之后每次render中都会忽略这个状态.如果写成getInitRule()函数调用的形式,会导致每次页面的render都会执行一次此函数,造成性能上的浪费
const getInitRule = () => [{ id: 1, name: '默认规则' }];
const RuleList = () => {
/** 错误的写法 */
const [ruleList, setRuleList] = useState(getInitRule());
return (
<ul>
{ruleList.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
正确地写法是,将函数的应用给到useState的即可,useState会在初始化render期间自动调用一次.
const [ruleList, setRuleList] = useState(getInitRule);
需要在调用 set 函数后获取 nextState
handleCLick() {
/** 设置一个变量缓存 nextState */
const nextState = count + 1
setCount(nextState)
console.log(nextState)
}
更新状态,页面没有重新渲染
react 通常会通过 Object.is()方法比较两次状态值,如果相同则不会更新,想要更新必须改变值的引用,用一个新的对象替换原来的状态
setObj({
...obj,
name: 'wsn'
});
如何将函数当做一个状态
const [fn setFn] = useState(() => someFunc)
handleClick() {
setFn(() => someOtherFunc)
}
以上是useState的相关用法,如有问题,欢迎指正.