Reputation: 139
I have recently put Redux into my app for the first time and thought I had it working but it seems to be returning empty arrays. I have checked my Postman get posts and it is working fine on the backend. Should my store be returning values if the arrays are empty like below?
What is likely the issue? I have a async Thunk action creator for it and a create slice Reducer that I thought were working ok.
If my index combineReducers that are createSlice are all appearing in white does this mean they aren't working correctly? The auth and message ones are in yellow and my login works correctly however I didn't use createSlice for them.
Update: I think this is to do with the syntax of my extraReducers "state: actionpayload.field". There is no error message flagging but i'm not sure it's doing what it is meant to do.
Or could this be to do with the fact that I have a combineReducer for my store and passing through reducers that are createSlice? (Should be configureStore for Redux toolkit) my Auth and messages work ok but they aren't Redux. Does configureStore allow both createSlice and normal switch statements at the same time?
index.js
export default combineReducers({
// combine the reducers
user,
fields,
article,
diveLog,
marineList,
diveSchool,
diveSpot,
admin,
auth,
message
});
reducer
const fieldsSlice = createSlice({
name: 'diveLogFields',
initialState: {
current: [],
region: [],
diveType: [],
visibility: [],
diveSpot: [],
},
reducers: {},
extraReducers: {
// picks up the success action from the thunk
[requireFieldData.fulfilled.type]: (state, action) => {
// set the property based on the field property in the action
(state: action.payload.field); (state: action.payload.items)
}
}
})
export default fieldsSlice.reducer;
action
export const requireFieldData = createAsyncThunk(
'fields/requireData', // action name
// action expects to be called with the name of the field
async (fields) => {
// you need to define a function to fetch the data by field name
const response = await diveLogFields(fields);
const { data } = response;
// what we return will be the action payload
return {
fields,
items: data.data
};
},
// only fetch when needed
{
condition: (fields, {getState}) => {
const {field} = getState();
// check if there is already data by looking at the array length
if ( field[fields].length > 0 ) {
// return false to cancel execution
return false;
}
}
}
)
Update
I am still getting the below error message when I try to render my page. I had to go into my store and add the compose Redux import as well.
Where does this message suggest the problem is?
Upvotes: 5
Views: 2631
Reputation: 139
Got it working. I had to change my store.js file to the below as it was previously set-up differently. I also used the code changes suggested in my replies.
const store = createStore(
rootReducer,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
Upvotes: 2
Reputation: 19813
I see few minor issues in your code, So, below are fixes and the explanations:
Slice:
const fieldsSlice = createSlice({
name: 'diveLogFields',
initialState: {
current: [],
region: [],
diveType: [],
visibility: [],
diveSpot: [],
fields: [], // Add initial fields array (if you want to store it)
items: [], // Add initial items array (if you want to store it)
},
reducers: {},
extraReducers: {
[requireFieldData.fulfilled.type]: (state, action) => {
state.fields = action.payload.fields
state.items = action.payload.items
},
},
})
export default fieldsSlice.reducer
In the above code, you can use state.fields = ...
and state.items = ...
to set data in state. It looks like we are directly mutating the state, but we are not because ...
Redux Toolkit allows us to write "mutating" logic in reducers. It doesn't actually mutate the state because it uses the Immer library, which detects changes to a "draft state" and produces a brand new immutable state based off those changes
AsyncThunkAction:
// Assumption, fake async call
function diveLogFields(fields) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ data: { data: [1, 2, 3] }, fields })
}, 2000)
})
}
export const requireFieldData = createAsyncThunk(
'diveLogFields/requireFieldData',
async (fields) => {
const response = await diveLogFields(fields)
const { data } = response
return {
fields,
items: data.data,
}
},
{
condition: (fields, { getState, extra }) => {
const { fields: fieldsState } = getState() // getState() returns your Redux State
if (fieldsState.fields.length > 0) {
return false // cancel the action if fields has some data
}
},
}
)
Here is an example to dispatch the async action:
function MyComponent() {
const dispatch = useDispatch()
// for example, make async call below hook
useEffect(() => {
dispatch(requireFieldData([4, 5, 6]))
}, [dispatch])
return (
<>i am MyComponent</>
)
}
Now, here is how the state looks like after dispatching the action:
Upvotes: 4