首页 > 解决方案 > 如何防止应用程序不渲染两次?(反应)

问题描述

我在我的应用程序中显示 a 时遇到问题totalAmount。由于两次渲染,它显示错误的值。我尝试为此使用 useMemo 钩子。

这就是它的样子(不要打扰 css :D )

在此处输入图像描述

所以它是这样计算的:

7.99 * 2 + 4.99 = 20.97

最后添加的项目 -44.99尚未计入totalAmount

2我猜它会因为渲染两次而将第一项乘以

这是我的代码沙箱:https ://codesandbox.io/s/objective-kare-805kc?file=/src/components/App.tsx

标签: reactjstypescriptuse-reducer

解决方案


您的组件对多次渲染没有弹性这一事实本身就是一个问题。通常,您不能假设您的组件只会渲染一定次数,当即将发布的并发模式发布时尤其如此。出于性能原因,减少渲染次数可能很有用,但我不认为这可以解决您的问题。

问题似乎是您的减速器不纯。reducer 应该是一个纯函数:它接受一个状态和一个动作,并产生一个没有副作用的新状态。但相反,在你的减速器中间你调用setFixedAmountand setTotalAmount,它将设置状态并导致重新渲染。

通过查看您的代码,我认为拥有三个状态是错误的。您有productstotalAmountfixedAmount,并且正在尝试编写代码以使它们彼此同步。相反,更好的方法是只有一个状态:products. totalAmount然后fixedAmount是根据产品计算得出的值。

function App() {
  const [allData] = useState(data.products);

  const [products, dispatch] = useReducer((state: any, action: any): any => {
    switch (action.type) {
      case "ADD_PRODUCT":
        state.filter((i: any) => {
          if (i.id === action.payload.id) {
            i.count = i.count + 1;
          }
          return i;
        });
        const noDuplicateArr = state.filter(
          (i: any) => i.id !== action.payload.id
        );
        return [...noDuplicateArr, action.payload];

      case "DELETE_PRODUCT":
        state.filter((i: any) => {
          if (i.id === action.payload.id) {
            i.count = 1;
          }
          return i;
        });
        return state.filter((item: any) => item.id !== action.payload.id);

      default:
        return state;
    }
  }, []);

  let totalAmount = 0;
  products.forEach(product => {
    totalAmount += product.count * product.price.amount;
  })
  const fixedAmount = totalAmount.toFixed(2);

  const store = useMemo(() => ({ products, dispatch }), [products]);

  return (
    <div className="app">
      <ProductContext.Provider value={{ allData, fixedAmount, store }}>
        <ShoppingList />
        <ShoppingBag />
      </ProductContext.Provider>
    </div>
  );
}

推荐阅读