I have all the needed working pieces of the React Redux-toolkit working but I can't seem to get my data to display in the component. I'm getting the error:
TypeError: noticesType.map is not a function 31 | {noticesType.map((post, index) => { | ^ 32 | <li key={index}>
When I see is that I'm most likely passing an object when it's looking for an array. I am pretty sure I have it set up correctly. I have The Redux-DevTools open, and it shows me the correct data coming back from the API call. After 3 days of searching the web, this is where I'm at. If there is more code or anything I've missed please let me know.
page.tsx
export default function page() {
const dispatch = useAppDispatch();
const { Notices, loading, error } = useAppSelector(
state => state.noticesReducer
)
console.log('Values: ')
console.log(Notices)
useEffect(() => {
dispatch(fetchNotices())
}, [dispatch])
return (
<>
<div>
<h1>Notices</h1>
<ul>
<>
{Notices.map((post, index) => {
<li key={index}>
{post.Title}
</li>
})}
</>
</ul>
</div>
</>
)
}
noticesTypes.ts Updated with JSON to TypeScript tool.
export interface Root {
Notices: Notice[]
CreatedDateTime: string
Version: number
Title: string
}
export interface Notice {
id: string
Title: string
Description: string
ChangeRecordNumber: any
StartDateTime: string
EndDateTime: string
Planned: boolean
ImpactType: string
}
notice.ts / noticeSlice
type NoticeState = Root & {
loading: boolean,
error: boolean,
}
const initialState: NoticeState = {
Notices: [],
loading: false,
error: false
}
export const fetchNotices = createAsyncThunk(
'fetchNotices',
async (thunkApi, { rejectWithValue }) => {
try {
const response = await fetch("https://API_CALL/notice")
return await response.json();
} catch (error) {
return rejectWithValue("Something isn't working");
}
}
);
export const noticeSlice = createSlice({
name: 'notices',
initialState,
reducers: {
setNotice: (state, action) => {
state.Notices = action.payload
},
getNotice: (state, action) => {
return action.payload.notices
},
},
extraReducers: (builder) => {
builder
.addCase(fetchNotices.pending, (state, action) => {
state.loading = true;
})
.addCase(fetchNotices.fulfilled, (state, action) => {
state.loading = false,
state.Notices = action.payload
})
.addCase(fetchNotices.rejected, (state, { payload }) => {
state.loading = true
})
}
})
export const {
getNotice,
setNotice,
} = noticeSlice.actions
export default noticeSlice.reducer
index.ts / store
export const store = configureStore({
reducer: {
noticesReducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware({ serializableCheck: false })
})
console.log(store.getState());
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
JSON coming back from API call.
{
"Notices": [
{
"id": "1",
"Title": "test title 1",
"Description": "Fake Notice: this is fake",
"StartDateTime": "2024-09-18",
"EndDateTime": "2024-09-26",
"Planned": true,
"ImpactedApps": [
{
"Id": "1",
"Name": "Api 1"
}
],
"IsActive": true
},
{
"id": "2",
"Title": "test title 2",
"Description": "Fake Notice: this is fake",
"StartDateTime": "2024-09-18",
"EndDateTime": "2024-09-26",
"Planned": true,
"ImpactedApps": [
{
"Id": "2",
"Name": "Api 2"
}
],
"IsActive": true
}
],
"CreatedDateTime": "2024-09-18",
"Version": 1,
"Title": "Notification Feed"
}
Here is an image of what's in the console when I console.log()
it out.
Here is the Notices
expanded
The issue is that you are not maintaining a valid state invariant. At some point during the execution of your code you modified state.Notices
from an array type to something else, likely the entire javascript object that is the response value from the fetchNotices
action.
Given the following response value:
{
"Notices": [
{
"id": "1",
"Title": "test title 1",
"Description": "Fake Notice: this is fake",
"StartDateTime": "2024-09-18",
"EndDateTime": "2024-09-26",
"Planned": true,
"ImpactedApps": [
{
"Id": "1",
"Name": "Api 1"
}
],
"IsActive": true
},
{
"id": "2",
"Title": "test title 2",
"Description": "Fake Notice: this is fake",
"StartDateTime": "2024-09-18",
"EndDateTime": "2024-09-26",
"Planned": true,
"ImpactedApps": [
{
"Id": "2",
"Name": "Api 2"
}
],
"IsActive": true
}
],
"CreatedDateTime": "2024-09-18",
"Version": 1,
"Title": "Notification Feed"
}
The featchNotices.fulfilled
reducer case is passing the entire response payload object and this breaks the invaint.
.addCase(fetchNotices.fulfilled, (state, action) => {
state.loading = false;
state.Notices= action.payload; // <-- not an array! 😞
})
The code should pass the nested Notices
array property to the state value.
.addCase(fetchNotices.fulfilled, (state, action) => {
state.loading = false;
state.Notices = action.payload.Notice; // <-- an array 🙂
})
Alternatively you could return the notices array directly from the Thunk action if you have no need for the other response values.
export const fetchNotices = createAsyncThunk('fetchNotices', async (thunkApi, { rejectWithValue }) => {
try {
const response = await fetch("https://API_CALL/notice")
const { Notices } = await response.json();
return Notices;
} catch (error) {
return rejectWithValue("Something isn't working");
}
});
.addCase(fetchNotices.fulfilled, (state, action) => {
state.loading = false;
state.Notices = action.payload; // <-- an array 🙂
})