在 react-toolkit 中使用 Immer 将状态设置为零填充数组
Setting state to a zero-filled array with Immer in react-toolkit
我一直在关注 TS usage docs,现在我正在尝试使用 Redux Toolkit 清除数组,但是我的 turnOffAll(state)
reducer 导致了数千次重新渲染(控制台日志输出)和一个关于它的错误浏览器。
我 read 可以 return
状态,而不是从 turnOffAll
中“改变”它。
因此,我尝试了 turnOffAll
的变体,具有:return Array(screenSize.width * screenSize.height).fill(0)
但这会导致 app.jsx 中出现错误,即 dispatch(turnOffAll());
没有提供足够的参数。
我在 turnOffAll
中尝试的另一种方法是 state = initialState;
但是虽然这不会导致不必要的重新渲染,但如果我在 [=39= 中执行 console.log(useAppSelector((state) => state.pixels));
则状态不会重置].
文件中的相关信息如下。此代码是我上面第一段中的情况,在从 app.jsx:
调用 dispatch(turnOffAll());
时会导致数千次重新渲染
pixelsSlice.ts:
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import type { RootState } from "./store";
import { Pixel, ScreenSize } from "./types";
//Defining a type for the slice state
interface PixelsState {
pixels: Array<number>;
}
const screenSize = { width: 128, height: 59 };
//Define the initial state using the above type.
const initialState: PixelsState = {
pixels: Array(screenSize.width * screenSize.height).fill(0),
};
export const pixelsSlice = createSlice({
name: "pixels",
initialState,
reducers: {
turnOn(state, action: PayloadAction<Pixel>) {
//This may look like mutating state, but Immer makes it ok:
state.pixels[
screenSize.width * action.payload.row + action.payload.column
] = 1;
},
turnOffAll(state) {
state = initialState;
},
},
});
export const { turnOn, turnOffAll } = pixelsSlice.actions;
//Other code such as selectors can use the imported `RootState` type...
export const selectPixels = (state: RootState) => state.pixels;
export default pixelsSlice.reducer;
app.tsx:
import React, { useEffect } from "react";
import { useAppSelector, useAppDispatch } from "./app/hooks";
import { turnOn, turnOffAll } from "./app/pixelsSlice";
import type { Pixel } from "./app/types";
function App() {
const dispatch = useAppDispatch();
//Demonstrating reading and writing pixels from the redux store.
console.log(useAppSelector((state) => state.pixels));
let p: Pixel = { row: 1, column: 2 };
dispatch(turnOn(p));
console.log(useAppSelector((state) => state.pixels));
dispatch(turnOffAll()); //THIS CAUSES THOUSANDS OF RERENDERS AND CONSOLE LOG OUTPUTS :(
console.log(useAppSelector((state) => state.pixels));
return <div className="App"></div>;
}
export default App;
hooks.ts:
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
您在这里违反了 React 的一个非常基本的规则——甚至 Redux 也不行:
您在渲染过程中触发了副作用。
React 可以多次触发该渲染函数而不将其提交给 DOM。它可以被触发,因为父组件重新渲染,因为状态改变,任何数量的原因。其中一个原因可能是您的 Redux 状态发生了变化。
因此,您分派、更改状态、触发重新渲染、再次分派,这会更改状态(设置为另一个空数组 - [] !== []
)并触发另一个渲染。
最重要的是,您的副作用如 dispatches
或状态 setter 调用 总是 发生在 useEffect
或事件中处理程序 - 在 useEffect
的情况下,您还使用了正确的依赖项数组 - 点击 React 文档。
此外,您的 dispatch
- useSelector
- 调度 useSelector
的工作流程可能不会在这里执行您想要的操作。状态可靠地只在渲染之间改变,而不是在渲染内。
除了@phry 的评论外,Immer-powered reducer 中的行 state =
总是不正确的。您需要改变 state
的 inside 值,或者 return 一个新值。
如果要替换现有状态,请执行 return initialState
。
有关详细信息,请参阅 the RTK usage guide page on "Writing Reducers with Immer"。
我一直在关注 TS usage docs,现在我正在尝试使用 Redux Toolkit 清除数组,但是我的 turnOffAll(state)
reducer 导致了数千次重新渲染(控制台日志输出)和一个关于它的错误浏览器。
我 read 可以 return
状态,而不是从 turnOffAll
中“改变”它。
因此,我尝试了 turnOffAll
的变体,具有:return Array(screenSize.width * screenSize.height).fill(0)
但这会导致 app.jsx 中出现错误,即 dispatch(turnOffAll());
没有提供足够的参数。
我在 turnOffAll
中尝试的另一种方法是 state = initialState;
但是虽然这不会导致不必要的重新渲染,但如果我在 [=39= 中执行 console.log(useAppSelector((state) => state.pixels));
则状态不会重置].
文件中的相关信息如下。此代码是我上面第一段中的情况,在从 app.jsx:
调用dispatch(turnOffAll());
时会导致数千次重新渲染
pixelsSlice.ts:
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import type { RootState } from "./store";
import { Pixel, ScreenSize } from "./types";
//Defining a type for the slice state
interface PixelsState {
pixels: Array<number>;
}
const screenSize = { width: 128, height: 59 };
//Define the initial state using the above type.
const initialState: PixelsState = {
pixels: Array(screenSize.width * screenSize.height).fill(0),
};
export const pixelsSlice = createSlice({
name: "pixels",
initialState,
reducers: {
turnOn(state, action: PayloadAction<Pixel>) {
//This may look like mutating state, but Immer makes it ok:
state.pixels[
screenSize.width * action.payload.row + action.payload.column
] = 1;
},
turnOffAll(state) {
state = initialState;
},
},
});
export const { turnOn, turnOffAll } = pixelsSlice.actions;
//Other code such as selectors can use the imported `RootState` type...
export const selectPixels = (state: RootState) => state.pixels;
export default pixelsSlice.reducer;
app.tsx:
import React, { useEffect } from "react";
import { useAppSelector, useAppDispatch } from "./app/hooks";
import { turnOn, turnOffAll } from "./app/pixelsSlice";
import type { Pixel } from "./app/types";
function App() {
const dispatch = useAppDispatch();
//Demonstrating reading and writing pixels from the redux store.
console.log(useAppSelector((state) => state.pixels));
let p: Pixel = { row: 1, column: 2 };
dispatch(turnOn(p));
console.log(useAppSelector((state) => state.pixels));
dispatch(turnOffAll()); //THIS CAUSES THOUSANDS OF RERENDERS AND CONSOLE LOG OUTPUTS :(
console.log(useAppSelector((state) => state.pixels));
return <div className="App"></div>;
}
export default App;
hooks.ts:
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
您在这里违反了 React 的一个非常基本的规则——甚至 Redux 也不行:
您在渲染过程中触发了副作用。
React 可以多次触发该渲染函数而不将其提交给 DOM。它可以被触发,因为父组件重新渲染,因为状态改变,任何数量的原因。其中一个原因可能是您的 Redux 状态发生了变化。
因此,您分派、更改状态、触发重新渲染、再次分派,这会更改状态(设置为另一个空数组 - [] !== []
)并触发另一个渲染。
最重要的是,您的副作用如 dispatches
或状态 setter 调用 总是 发生在 useEffect
或事件中处理程序 - 在 useEffect
的情况下,您还使用了正确的依赖项数组 - 点击 React 文档。
此外,您的 dispatch
- useSelector
- 调度 useSelector
的工作流程可能不会在这里执行您想要的操作。状态可靠地只在渲染之间改变,而不是在渲染内。
除了@phry 的评论外,Immer-powered reducer 中的行 state =
总是不正确的。您需要改变 state
的 inside 值,或者 return 一个新值。
如果要替换现有状态,请执行 return initialState
。
有关详细信息,请参阅 the RTK usage guide page on "Writing Reducers with Immer"。