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?
@aw04 but then it will just add '[]' as a new element in the array. If you want the next state to be an empty array, then just return an empty array. return [] would make the state equal to your default state.
Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state. Using local component state is fine.
It's simple to get access to the store inside a React component – no need to pass the store as a prop or import it, just use the connect function from React Redux, and supply a mapStateToProps function that pulls out the data you need. Then, inside the component, you can pass that data to a function that needs it.
It's stored in browser's memory.
Applying redux reducers to arrays The redux pattern provides a simple (and strict) pattern for managing state; a single store holds the state while a reducer applies actions, evolving the application state.
The redux pattern provides a simple (and strict) pattern for managing state; a single store holds the state while a reducer applies actions, evolving the application state.
More... so now we have an issue which is: Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers. . also, this type of error can be if we mistake the file spelling or we forgot to import the reducer file into the store file or if made mistake in including the file path.
In order to render multiple counter instances, this component has to be stateless. With the way the redux connectors work, this is a trivial change to make. The component render function remains the same, but the state and dispatch property mappings are removed: Finally, a new CounterCollection component is added that renders the application state:
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:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With