优秀的编程知识分享平台

网站首页 > 技术文章 正文

React开发者在2024年依然会犯的12个useState和useEffect错误

nanyue 2024-08-05 19:52:33 技术文章 10 ℃

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一起搞技术。

最近发表
标签列表