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