我有一个用 CRA 引导的 react 应用程序,我使用 @reduxjs/toolkit 库通过 reduxjs 实现了状态管理。该状态当前由 4 个不同的切片组成。
我现在的问题是,如果我更改切片的单个属性,订阅其他切片的组件,通过 useSelector 钩子,会重新渲染,导致各种我没想到的奇怪行为。
通常,我从组件中订阅切片的方式如下:
const slice1 = useSelector(({ slice1 }: RootState) => slice1)
其中 RootState 类型表示组成存储的所有切片,结合 combineReduce 函数,如下所示:
const rootReducer = combineReducers({
slice1: SliceOneReducer,
slice2: SliceTwoReducer,
...
})
我的印象是,在选择切片形式时使用解构可以避免组件在其他切片发生变化时重新渲染。
所以我的问题是:这是正确的行为还是我以错误的方式做事?
编辑对于效果管理,我通过以下方式添加了 redux-observable 中间件,因为我不太喜欢 thunk:
const rootEpic: any = combineEpics(
...SliceOneEpics
)
然后在配置商店时:
const epicMiddleware = createEpicMiddleware();
const store = configureStore({
reducer: reducers,
middleware: getDefaultMiddleware =>
getDefaultMiddleware().prepend(
epicMiddleware
)
})
epicMiddleware.run(rootEpic);
中间件会不会把事情搞砸?也许以这种方式指定中间件会删除 @reduxjs/toolkit 提供的 immer 的默认实现?
编辑 2:根据 Mary 的评论,这是基本上导致所有其他切片返回新对象的减速器。在这种情况下,具体操作是 setShowQRCode。但是对于任何其他操作,行为都是相同的。我正在直接更新状态,因为 immer 在窗帘后面施展魔法
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export interface UIState {
themeLoaded?: boolean,
layoutLoaded?: boolean,
showQrCode?: boolean,
showFloatingButton?: boolean,
moduleLoading?: boolean
}
const uiSlice = createSlice({
name: 'uistate',
initialState: {} as UIState,
reducers: {
setUIState(state: UIState, { payload }: PayloadAction<UIState>) {
Object.keys(payload).forEach(key => state[key] = payload[key])
},
setThemeLoaded(state: UIState, { payload }: PayloadAction<boolean>) {
state.themeLoaded = payload
},
setLayoutLoaded(state: UIState, { payload }: PayloadAction<boolean>) {
state.layoutLoaded = payload
},
setShowQRCode(state: UIState, { payload }: PayloadAction<boolean>) {
state.showQrCode = payload
}
}
})
export const { setUIState, setThemeLoaded, setLayoutLoaded, setShowQRCode } = uiSlice.actions
export default uiSlice.reducer