【问题标题】:Component not re rendering when value from useContext is updated更新 useContext 中的值时组件不会重新呈现
【发布时间】:2020-01-10 07:50:04
【问题描述】:

我正在使用 React 的上下文 api 来存储项目数组。有一个组件可以通过 useContext() 访问这个数组并显示数组的长度。还有另一个组件可以访问该函数以通过 useContext 更新此数组。将项目添加到数组时,组件不会重新渲染以反映数组的新长度。当我导航到应用程序中的另一个页面时,组件会重新呈现并反映数组的当前长度。每当上下文中的数组发生变化时,我都需要重新渲染组件。

我尝试过使用 Context.Consumer 而不是 useContext,但是当数组更改时它仍然不会重新渲染。

//orderContext.js//

import React, { createContext, useState } from "react"

const OrderContext = createContext({
  addToOrder: () => {},
  products: [],
})

const OrderProvider = ({ children }) => {
  const [products, setProducts] = useState([])

  const addToOrder = (idToAdd, quantityToAdd = 1) => {
    let newProducts = products
    newProducts[newProducts.length] = {
      id: idToAdd,
      quantity: quantityToAdd,
    }
    setProducts(newProducts)
  }

  return (
    <OrderContext.Provider
      value={{
        addToOrder,
        products,
      }}
    >
      {children}
    </OrderContext.Provider>
  )
}

export default OrderContext
export { OrderProvider }
//addToCartButton.js//

import React, { useContext } from "react"
import OrderContext from "../../../context/orderContext"

export default ({ price, productId }) => {
  const { addToOrder } = useContext(OrderContext)

  return (
    <button onClick={() => addToOrder(productId, 1)}>
      <span>${price}</span>
    </button>
  )
}

//cart.js//

import React, { useContext, useState, useEffect } from "react"
import OrderContext from "../../context/orderContext"

export default () => {
  const { products } = useContext(OrderContext)
  return <span>{products.length}</span>
}
//gatsby-browser.js//

import React from "react"
import { OrderProvider } from "./src/context/orderContext"
export const wrapRootElement = ({ element }) => (
   <OrderProvider>{element}</OrderProvider>
)

我希望购物车组件会在更新数组时显示数组的新长度,但在我导航到另一个页面时重新渲染组件之前它保持不变。每次更新上下文中的数组时,我都需要它重新渲染。

【问题讨论】:

  • 问题很可能是你正在改变数组,而不是设置一个新数组,所以 React 使用浅等式将数组视为相同

标签: reactjs gatsby react-context


【解决方案1】:

问题很可能是您正在改变数组(而不是设置新数组),因此 React 使用浅等式将数组视为相同。

更改您的 addOrder 方法以分配新数组应该可以解决此问题:

const addToOrder = (idToAdd, quantityToAdd = 1) =>
  setProducts([
    ...products,
    {
      id: idToAdd,
      quantity: quantityToAdd
    }
  ]);

【讨论】:

  • 我相信我的函数已经分配了一个新数组而不是变异const addToOrder = (idToAdd, quantityToAdd = 1) =&gt; { let newProducts = products newProducts[newProducts.length] = { id: idToAdd, quantity: quantityToAdd, } setProducts(newProducts) }
  • 你可以看到这里不是这样,它正在被变异:codesandbox.io/s/array-mutation-5oocr
  • 我不明白它为什么会发生变异,因为我正在创建一个新数组并对其进行变异,然后将状态设置为新数组。我使用了您的解决方案,并且成功了,非常感谢您的帮助。
  • 同一个数组被分配给不同的变量/参数。所以它可能有不同的变量名,但它始终是同一个数组。
  • let newProducts = products - 这是一个引用分配。对newProducts 所做的任何修改也会影响products。为避免改变原始数组,请始终创建一个副本 - let newProducts = [ ...products ]。不过,我不会在您的情况下推荐这种方法,因为这是不必要的步骤。只需遵循@skovy 的建议即可:)
【解决方案2】:

正如@skovy 所说,如果您想更改原始数组,可以根据他的回答有更优雅的解决方案。

setProducts(prevState => {
  prevState[0].id = newId
  return [...prevState]
})

【讨论】:

    【解决方案3】:

    @skovy 的描述帮助我理解了为什么我的组件没有重新渲染。

    在我的例子中,我有一个提供者拥有一个大字典,并且每次我更新该字典时都不会发生重新渲染。

    例如:

    const [var, setVar] = useState({some large dictionary});
    
    ...mutate same dictionary
    setVar(var) //this would not cause re-render
    setVar({...var}) // this caused a re-render because it is a new object
    

    我会厌倦在大型应用程序上执行此操作,因为重新渲染会导致严重的性能问题。在我的情况下,它是一个小的两页小程序,所以一些浪费的重新渲染对我来说是可以的。

    【讨论】:

      猜你喜欢
      • 2021-01-26
      • 2021-07-10
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      • 1970-01-01
      • 2018-04-09
      相关资源
      最近更新 更多