首页 > 解决方案 > Redux Toolkit `useAppSelector` 值在未更新时会导致渲染

问题描述

背景

标题可能令人困惑,但为了解释,我将切片与 Redux Toolkit 一起使用以管理状态。这些切片为我的地图应用程序保存各种状态值。具体来说,我在渲染方面遇到的两个问题是单击位置(称为focusedPosition鼠标坐标mouseCoords)。这些切片中的每一个都包含一个latlng值。使用react-leaflet,我有地图事件,focusedPosition每当用户单击地图以显示弹出窗口时,它们都会更新。我还有一个事件来捕获mouseCoords要显示在地图一角的事件。但是,由于某种原因,任何订阅focusedPosition更新的组件都会在鼠标移动时重新渲染——即使它没有订阅mouseCoords. 这会导致多个问题,包括性能问题以及映射弹出窗口闪烁,因为它们会在鼠标移动时不断重新渲染。如果我在 react-leaflet 中注释掉mousemove事件,问题就会停止,因为该值不再更新,但这不是一个选项,因为我真的需要捕获这些鼠标坐标。

我怎样才能确定为什么这两个值会以某种方式联系起来,我该如何解决这个问题?

适用的代码如下,以及一个代码沙箱

store.ts
export const store = configureStore({
  reducer: {
    focusedPosition: focusedPositionReducer,
    mouseCoords: mouseCoordsReducer,
  }
})
// Export AppDispatch
// Export RootState
// Export AppThunk
聚焦位置切片.tsx
interface FocusedPositionState {
  lat: number | null
  lng: number | null
}
const initialState: FocusedPositionState = {
  lat: null,
  lng: null,
}
export const focusedPositionSlice = createSlice({
  name: 'focusedPosition',
  initialState,
  reducers: {
    clearFocusedPosition: state => {
      state.lat = null
      state.lng = null
    },
    setFocusedPosition: (state, action: PayloadAction<FocusedPositionState>) => {
      state.lat = action.payload.lat
      state.lng = action.payload.lng
    }
  }
})
// Export actions
// Export getFocusedPosition selector
// Export default reducer
mouseCoordsSlice.tsx
interface MouseCoordsState {
  lat: number
  lng: number
}
const initialState: MouseCoordsState = {
  lat: 0,
  lng: 0,
}
export const mouseCoordsSlice = createSlice({
  name: 'mouseCoords',
  initialState,
  reducers: {
    setMouseCoords: (state, action: PayloadAction<MouseCoordsState>) => {
      state.lat = action.payload.lat
      state.lng = action.payload.lng
    }
  }
})
// Export actions
// Export getMouseCoords selector
// Export default reducer

标签: reactjsredux-toolkitreact-leaflet-v3

解决方案


getFocusedPosition的选择器在每次调用 reducer 时都会创建一个新对象。

由于 react-redux 会在何时重新渲染,oldSelectorResult !=== newSelectorResult并且这些对象在引用上不相等,这将导致重新渲染。

您可以选择完整的切片状态(如果将来您添加更多道具,可能会出现过度使用的风险)

export const getFocusedPosition = (state: RootState) => state.focusedPosition;

或创建一个仅在输入值更改时返回新对象的记忆选择器(请参阅https://redux.js.org/recipes/computing-derived-data):

export const getFocusedPosition = createSelector(
  state => state.focusedPosition.lat,
  state => state.focusedPosition.lng,
  (lat, lng) => ({ lat, lng })
)

或者您只需单独订阅这两个值:

const lat = useAppSelector(state => state.focusedPosition.lat)
const lng = useAppSelector(state => state.focusedPosition.lng)

所有这些都在useSelector文档中进一步讨论:https ://react-redux.js.org/api/hooks#equality-comparisons-and-updates


推荐阅读