「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」。
上一篇请看这里
快照
state可以看成是快照,只与本次渲染有关
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
上面代码onClick的事件处理函数调用了三次setNumber,那么最终点击结束<h1>{number}</h1>
的值是什么?
答案是1.具体源码见这里
{
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}
当执行这段代码时,其实是执行了
{
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
}
同一个函数执行了3次。
同理,因此下面的代码,完整版
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setTimeout(() => {
alert(number);
}, 3000);
}}>+5</button>
</>
)
}
<h1>
后面的number是5,但是alert出来的是0
传函数
setState除了传值,还可以传函数,函数的入参是之前的state值,比如下面setNumber里面改成函数。函数的执行时机在事件处理函数之后.
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber((n) => {
console.log("setNumber callback");
return n + 1;
});
setNumber(n => n + 1);
setNumber(n => n + 1);
console.log("event handler")
}}>+3</button>
</>
)
}
onClick点击之后,number的值就变成了3,[完整源码],(codesandbox.io/s/sandpack-…)
先打印event handler,再打印setNumber callback.
因此可以把setState(x),看成setState(x=>n=>x)一种特殊的函数,之前的state的值根本没用。
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
这里的number是42.
第一行,number变成5,第二行number变成6,第三行number直接赋值42.
批处理,为了提升性能,上面即使调用了3次setNumber,最终只会触发一次渲染。
在setState里面更新对象和数组,用纯函数方式。
位置
React组件的状态和在JSX里面的位置有关。
React把组件转成ReactElement。
因此,如果有一个条件判断,返回了2个同类型但是参数不同的组件,React也会把它认为是同一个组件的不同参数。
例如下面的例子,查看完整代码这里
export default function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? (
<Counter isFancy={true} />
) : (
<Counter isFancy={false} />
)}
<label>
<input
type="checkbox"
checked={isFancy}
onChange={e => {
setIsFancy(e.target.checked)
}}
/>
Use fancy styling
</label>
</div>
);
}
这行{isFancy ? ( <Counter isFancy={true} /> ) : ( <Counter isFancy={false} /> )}
虽然貌似根据不同的情况,返回不同的组件,但是在react里面,由于三元运算符操作之后,还是返回<Counter/>
,两次render之间,只是isFancy的变化,因此React就会认为是同一个组件,因为之前组件的状态会被保留。
如何避免这个情况
- 加关键字key。
{isPlayerA ? (
<Counter key="Taylor" person="Taylor" />
) : (
<Counter key="Sarah" person="Sarah" />
)}
- 结构不同
{isPlayerA &&
<Counter person="Taylor" />
}
{!isPlayerA &&
<Counter person="Sarah" />
}
组件不要定义在组件里面
例如下面,完整看这里
import { useState } from 'react';
export default function MyComponent() {
const [counter, setCounter] = useState(0);
function MyTextField() {
const [text, setText] = useState('');
return (
<input
value={text}
onChange={e => setText(e.target.value)}
/>
);
}
return (
<>
<MyTextField />
<button onClick={() => {
setCounter(counter + 1)
}}>Clicked {counter} times</button>
</>
);
}
MyTextField
这个组件每次render都会被重置,因此无法保留状态。