在调度、Redux 工具包和 selectors/reselect 之间检测到状态突变

A state mutation was detected between dispatches, Redux toolkit and selectors/reselect

我在 createSelector 和异步 thunk 方面遇到了一些麻烦。

基本上我正在尝试检索链接列表,并根据点击次数从高到低对它们进行排序。

这是我的简化代码。

MainPage.tsx

export const MainPage = (): JSX.Element => {
  const dispatch = useDispatch();
  const allLinks = useSelector(selectLinks);
  
  useEffect(() => {
    dispatch(getLinks());
  }, [dispatch]);

  return (
    <div>
      {allLinks.map((link) => ()} //unrelated logic
    </div>
  );
};

links.actions.ts

export const getLinks = createAsyncThunk(
  "getLinks",
  async () => {
    const links = await axios.get"/links")
    return links;
  },
);

links.slice.ts

const linksSlice = createSlice({
  name: "data",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
    .addCase(
      getLinks.fulfilled,
      (state, action) => {
        state.links = action.payload;
      },
    );
  },
});

links.selectors.ts

const selectLinksState = (state: RootState) => state.links;

export const selectLinks = createSelector(selectLinksState, (state) =>
  state.links.sort((a, b) => b.clickCount - a.clickCount),
);

因此编译正常,但是当我在网络浏览器中打开它时,出现以下错误Error: Invariant failed: A state mutation was detected between dispatches

有谁知道我做错了什么?我很确定这与选择器有关,因为如果我在操作中获取链接后直接对链接进行排序,它就可以正常工作。

所以即使 Redux 工具包是可变的,我还是修改了 reselect 函数并将其更改为:

export const selectLinks = createSelector(selectLinksState, (state) => {
  const sortedArray = [...state.links].sort(
    (a, b) => b.clickCount - a.clickCount,
  );
  return sortedArray;
});

我的逻辑是创建一个新数组,这样它就不会引起任何突变问题。现在可以正常使用了!

排序使用就地排序算法,它不是 return 新数组,因此您是通过选择器逻辑改变状态。

Array.prototype.sort

The sort() method sorts the elements of an array in place and returns the sorted array. The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.

您可以使用 .slice 内联创建 links 数组的副本,然后对 new 数组引用进行排序:

export const selectLinks = createSelector(
  [selectLinksState],
  (state) => state.links.slice().sort((a, b) => b.clickCount - a.clickCount),
);