Hello,大家好,我是前端老法师!
今天在Youtube上看到了一个关于使用useState和useEffect场景错误的视频,觉的很不错,决定用文字版本分享给大家。同时建议大家观看原版视频,链接地址:https://www.youtube.com/watch?v=-yIsQPp31L0。
state的更新并非实时
初级React开发者,比较容易犯改错误,因为从语法糖上很难体现state更新的异步特征,虽然官方文档做了说明,但是还是容易被大家忽视。示例代码如下:
export default function Demo(){
const [state,setState] = useSate(0);
const click = ()=>{
setState(state+1);
setState(state+1);
}
return <div>
<button onClick={click}>click</button>
</div>
}
上面的代码,预期是希望一次点击,state的值+2。但是实际的情况是依然是每次只是+1。
原因就是第一个setState执行之后,第二个setState执行的时候,state依然保持不变。
条件渲染
useSate和useEffect,在React官方文档指出,每次组件进行redner或者re-render的时候,这个两个hooks必须执行。因此如下代码是错误的:
export default function Demo(props){
if(props.id){
return <div>has id props</div>
}
const [a,setA] = useState(0);
useEffect(()=>{
// ...
},[])
return <div>
<button onClick={click}>click</button>
</div>
}
上面的代码就不符合React官方定的规则,当props.id为true当时候,useState和useEffect就无法得到执行。
更新对象状态
当state是一个对象的时候,初级开发者容易犯如下错误:
export default function Demo(){
console.log('render...')
const [obj,setObj] = useState({
name:'tom',
age:18
});
const click = ()=>{
obj.name = 'jack'
console.log(obj);
setObj(obj);
}
return <div>
{obj.age}<br/>
{obj.name}
<button onClick={click}>click</button>
</div>
}
点击事件触发了,obj的值也发生了变化,但是没有触发组件的render。这是因为React这个时候比对的是对象的引用,setObj的时候,引用的值没有发生变化,因此无法触发render。
当我们有多个状态的时候,可以使用object替代
注意:这个不是所有场景都适用。表单场景最佳
import { ChangeEvent, useState} from 'react'
export default function Demo(){
const [username,setUsername] = useState('');
const [password,setPassword] = useState('');
const nameChange = (e: ChangeEvent<HTMLInputElement>)=>{
console.log(e.target.value)
setUsername(e.target.value);
}
const passwrodChange = (e: ChangeEvent<HTMLInputElement>)=>{
setPassword(e.target.value)
}
return <div>
<form>
<input type='text' value={username} name="username" onChange={e=>nameChange(e)}/>
<input type='password' value={password} name="password" onChange={e=>passwrodChange(e)}/>
</form>
</div>
}
使用object状态替换
import { ChangeEvent, useState} from 'react'
export default function Demo(){
const [form,setForm] = useState({
username:'',
password:''
})
const handleFromChange = (e: ChangeEvent<HTMLInputElement>)=>{
setForm({
...form,
[e.target.name]:e.target.value
})
}
return <div>
<form>
<input type='text' value={form.username} name="username" onChange={e=>handleFromChange(e)}/>
<input type='password' value={form.password} name="password" onChange={e=>handleFromChange(e)}/>
</form>
</div>
}
这个场景不需要使用useEffect
示例代码如下
import { useEffect, useState} from 'react';
const X = 5;
export default function Demo(){
const [price,setPrice] = useState(1);
const [totalPrice,setTotalPrice] = useState(price*X);
const add = ()=>{
setPrice(price+1);
}
useEffect(()=>{
setTotalPrice(price*X);
},[price])
return <div>
<button onClick={add}>Add</button>
{
totalPrice
}
</div>
}
直接使用 render来计算
import { useEffect, useState} from 'react';
const X = 5;
export default function Demo(){
const [price,setPrice] = useState(1);
const totalPrice = price * X;
const add = ()=>{
setPrice(price+1);
}
return <div>
<button onClick={add}>Add</button>
{
totalPrice
}
</div>
}
当状态为基础类型和非基础类型是,触发render
基础类型当值相等的时候不会触发render,但是非基础类型即使值一样,也会触发render。示例代码如下:
export default function Demo(){
const [price,setPrice] = useState(1);
const [obj,setObj] = useState({
name:'tom'
})
const add = ()=>{
setPrice(1); // 不会触发render
}
const add2 = ()=>{
// 依然会触发render
setObj({
name:'tom'
})
}
return <div>
<button onClick={add}>Add</button>
<button onClick={add2}>Add2</button>
</div>
}
对象作为状态的时候,要做好初始化,否则使用的时候容易引起异常
import { useEffect, useState} from 'react';
export default function Demo(){
const [obj,setObj] = useState()
useEffect(()=>{
setObj({
name:'123'
})
},[])
return <div>
{/* Cannot read properties of undefined (reading 'name') */}
{obj.name}
</div>
}
还剩下5个,下次再写。[再见]
如果文章对你有帮助,欢迎【评论】和【关注】,也可以私信加V一起搞技术。