Как сделать так, чтобы приложение не отображалось дважды? (reactjs) - PullRequest
0 голосов
/ 28 апреля 2020

У меня проблема с отображением totalAmount в моем приложении. Из-за двухкратного рендеринга отображается неверное значение. Я пытаюсь использовать хук useMemo для этого.

Вот как это выглядит (не беспокойтесь о css: D)

enter image description here

Таким образом, это считается следующим образом:

7.99 * 2 + 4.99 = 20.97

последний добавленный элемент - 44.99 не был засчитан в totalAmount

и умножает первый элемент на 2 из-за рендеринга дважды, я думаю

А вот моя песочница для кода: https://codesandbox.io/s/objective-kare-805kc?file= / src / components / App.tsx

1 Ответ

3 голосов
/ 28 апреля 2020

Тот факт, что ваши компоненты не устойчивы к нескольким рендерам, сам по себе является проблемой. В общем, вы не можете предполагать, что ваш компонент будет рендериться только определенное количество раз, и это станет особенно актуальным, когда выйдет следующий параллельный режим. Уменьшение количества рендеров может быть полезно по соображениям производительности, но я не считаю, что это исправит вашу проблему.

Проблема в том, что ваш редуктор нечистый. Редуктор должен быть чистой функцией: он принимает состояние и действие и создает новое состояние без побочных эффектов. Но вместо этого, в середине вашего редуктора вы вызываете setFixedAmount и setTotalAmount, что установит состояние и вызовет повторное рендеринг.

Из-за просмотра вашего кода, я считаю ошибкой иметь три штуки гос. У вас есть products, totalAmount и fixedAmount, и вы пытаетесь написать код, чтобы держать их всех в синхронизации c друг с другом. Вместо этого лучше всего иметь одно состояние: 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>
  );
}
...