Redux:从 slice reducer 操作调用 thunk 操作

Redux: Call thunk action from slice reducer action

我有一个按需加载子项的树结构,这是我的减速器。我遇到的问题是,当我想从 toggleExpandedProp 调用我的 thunk 操作时,我得到异常(见下文)。我该怎么办?

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { useDispatch } from 'react-redux';
import axios from 'axios';

const dispatch = useDispatch()

export const getRoot = createAsyncThunk('data/nodes/getRoot', async () => {
    const response = await axios.get('http://localhost:5000/api/nodes/root');
    const data = await response.data;
    return data;
});

export const getChildren = createAsyncThunk('data/nodes/getRoot', async params => {
    const response = await axios.get('http://localhost:5000/api/nodes/' + params.id + '/children');
    const data = await response.data;
    return data;
});

const initialState = {
    data: [],
    loading: 'idle'
};

// Then, handle actions in your reducers:
const nodesSlice = createSlice({
    name: 'nodes',
    initialState,
    reducers: {
        toggleExpandedProp: (state, action) => {
            state.data.forEach(element => {
                if(element.id === action.payload.id) {
                    element.expanded = !element.expanded;
                    dispatch(getChildren(element));
                }
            });
        }
    },
    extraReducers: {
      // Add reducers for additional action types here, and handle loading state as needed
      [getRoot.fulfilled]: (state, action) => {
        state.data = action.payload;
      },
      [getChildren.fulfilled]: (state, action) => {
        state.data.push(action.payload);
      }
    }
  })

export const { toggleExpandedProp } = nodesSlice.actions;

export default nodesSlice.reducer;

发生异常。 错误:无效挂钩调用。钩子只能在函数组件的主体内部调用。这可能是由于以下原因之一造成的:

  1. 您的 React 和渲染器版本可能不匹配(例如 React DOM)
  2. 您可能违反了 Hooks 规则
  3. 您可能在同一个应用程序中拥有多个 React 副本 有关如何调试和修复此问题的提示,请参阅 https://reactjs.org/link/invalid-hook-call
const dispatch = useDispatch()

您只能在函数组件内部或另一个挂钩内部使用 useDispatch。您不能像这样在文件的顶层使用它。

你不应该从 reducer 内部调用 dispatch。但是 dispatch 一个 thunk 的多个动作是可以的。所以你可以把 toggleExpandedProp 变成一个 thunk 动作。

您可能需要重新考虑其中的一些逻辑。在扩展节点时从 API 中获取子节点然后在折叠节点时再次获取它们真的有意义吗?

export const toggleExpandedProp = createAsyncThunk(
  "data/nodes/toggleExpandedProp",
  async (params, { dispatch }) => {
    dispatch(getChildren(params));
  }
);

这是一个无用的 thunk,因为我们实际上 return 什么都没有。您可以将它与 getChildren 操作结合使用,还是您也需要单独调用该操作?

const nodesSlice = createSlice({
  name: "nodes",
  initialState,
  reducers: {
  },
  extraReducers: {
    [toggleExpandedProp.pending]: (state, action) => {
      state.data.forEach((element) => {
        if (element.id === action.payload.id) {
          element.expanded = !element.expanded;
        }
      });
    },
    [getRoot.fulfilled]: (state, action) => {
      state.data = action.payload;
    },
    [getChildren.fulfilled]: (state, action) => {
      state.data.push(action.payload);
    }
  }
});