为了账号安全,请及时绑定邮箱和手机立即绑定

在 setState 挂钩中更改 prev 状态更新视图,而无需重新呈现。为什么?

在 setState 挂钩中更改 prev 状态更新视图,而无需重新呈现。为什么?

繁星点点滴滴 2022-09-23 09:40:17

我的更改产品名称函数称为 setState,它返回“突变的上一个状态”。我将函数传递给子组件,并通过上下文 API 在子组件中调用它。该函数已成功更新了子级和父级中显示的产品名称,但父级确实触发了重新渲染。父级如何在不重新渲染的情况下更新视图?谁能解释一下设置状态中的上一个状态实际上是什么?


const App = () => {

  const [products, setProducts] = useState(initialValues);


  const changeProductName = (id, newName) => {

    setProducts((prevState) => {   //is preState a copy of state?  

      prevState.products.filter(

        (product) => product.id === id 

      )[0].name = newName;        //Mutates prevState

      return prevState;           //Did I return a new state?

    });

  };


  useEffect(() => 

    console.log("I would know when App re-renders")); //No re-render!


  return (

    <>    //Some React Switch and Routers 

    <div>

      {product.map(product=>product.name)}   //Successfully Updated!

    </div>

    <ProductContext value={(products, changeProductName)}> 

      <ProductPage />     //call changeProductName and it works!

    </ProductContext>

    </>   

  ); 

};


如果我更改的函数不触及 prevState,父级将按预期重新呈现。这种方法更好吗?


  //this will trigger parent re-render.

  const changeProductName = (id, newName) => { 

    setProducts((prevState) => {

      prevState.products.filter(

        (product) => product.id === id

      )[0].name = newName;

      return prevState;

    });

  };


查看完整描述

2 回答

?
慕田峪7331174

TA贡献1486条经验 获得超13个赞

据我所知,改变国家通常是一个坏主意。

根据这个答案,改变状态可能不会导致重新渲染,因为在突变过程中对状态对象的引用不会改变。

我宁愿使用某种类似 redux 的不可变模式:

const changeProductName = (id, newName) => { 

  setProducts((prevState) => (

    prevState.map(product=>{

      if(product.id!==id){

        // name is not changed since the id does not match

        return product;

      } else {

        // change it in the case of match

        return {...product, name:newName}

      }

    }

  )

}


查看完整回答
反对 回复 2022-09-23
?
红糖糍粑

TA贡献1518条经验 获得超4个赞

谁能解释一下设置状态中的上一个状态实际上是什么?


prev 状态是对前一个状态的引用。它不是状态的副本,它是位于状态内部的对象的引用。因此,更改该对象不会更改对象引用。


因此,它不应该直接突变。相反,应通过基于 prevState 的输入构建新对象来表示更改。


例如,如果您在更改中进行检查,例如:


setProducts(prevState => {

  prevState.filter(product => product.id == id)[0].name = newName;

  console.log(prevState === products); // This will console true

  return prevState;

}); 

另外,由于您正在使用钩子,因此当您编写...本身已经是产品了。因此,当您尝试访问 时,您将在示例中得到未定义的错误。setProducts((prevState) => { prevState.products}prevState.products


所以我建议你这样做:


  const changeProductName = (id, newName) => {

    setProducts(prevProducts =>

      prevProducts.map(product =>

        product.id === id ? { ...product, name: newName } : product

      )

    );

  };

.map将基于 prevState 构建一个新数组,并更改在函数中调用 ID 的产品的名称。


查看完整回答
反对 回复 2022-09-23

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信