Reputation: 819
I have a tree structure which is loading children on demand, this is my reducer. The problem I have is that when I want to call my thunk action from toggleExpandedProp I get exception (see bellow). What should I do?
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;
Exception has occurred. Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
Upvotes: 4
Views: 5128
Reputation: 42188
const dispatch = useDispatch()
You can only use useDispatch
inside of a function component or inside another hook. You cannot use it at the top-level of a file like this.
You should not call dispatch
from inside a reducer. But it's ok to dispatch
multiple actions from a thunk. So you can turn toggleExpandedProp
into a thunk action.
You probably need to rethink some of this logic. Does it really make sense to fetch children from an API when expanding a node and then fetch them again when collapsing it?
export const toggleExpandedProp = createAsyncThunk(
"data/nodes/toggleExpandedProp",
async (params, { dispatch }) => {
dispatch(getChildren(params));
}
);
This is kind of a useless thunk since we don't actually return anything. Can you combine it with the getChildren
action, or do you need to call that action on its own too?
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);
}
}
});
Upvotes: 4