I am trying to update an array within the stories array in my state. I want the payload object to come to the top of the stories array containing it. I tried the method below but it keeps on adding the payload multiple times to the array.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { StoriesService } from "../../services/stories.service";
export const createStory = createAsyncThunk(
"stories/createStory",
async (arg, thunkAPI) => {
const data = JSON.stringify(arg);
try {
await StoriesService.createStory(data);
return thunkAPI.fulfillWithValue({ msg: "Story upload successful" });
} catch (error) {
if (error.response.status === 413) {
return thunkAPI.rejectWithValue({ msg: "Upload size too large!" });
}
if (error.response.data.errors) {
const data = error.response.data.errors[0];
return thunkAPI.rejectWithValue(data);
}
return thunkAPI.rejectWithValue({
msg: "An error occured. Please try again",
});
}
}
);
export const deleteStory = createAsyncThunk(
"stories/deleteStory",
async (arg, thunkAPI) => {
try {
await StoriesService.deleteStory(arg);
thunkAPI.dispatch(getLoggedInUserStories);
return thunkAPI.fulfillWithValue({ msg: "Story deleted successfully" });
} catch (error) {
return thunkAPI.rejectWithValue({
msg: "An error occured while deleting story",
});
}
}
);
export const getLoggedInUserStories = createAsyncThunk(
"stories/getLoggedInUserStories",
async (arg, thunkAPI) => {
try {
const response = await StoriesService.getLoggedInUserStories(
thunkAPI.signal
);
return response.data;
} catch (error) {
return thunkAPI.rejectWithValue({
msg: "An error occured.",
});
}
}
);
export const getFeedStories = createAsyncThunk(
"stories/getFeedStories",
async (arg, thunkAPI) => {
try {
const response = await StoriesService.getFeedStories(thunkAPI.signal);
return response.data;
} catch (error) {
return thunkAPI.rejectWithValue({
msg: "An error occured.",
});
}
}
);
const initialState = {
isUploadingStory: false,
isDeletingStory: false,
userStories: [],
stories: [],
showStoryCreateModal: false,
};
const storiesSlice = createSlice({
name: "stories",
initialState,
reducers: {
clearStories(state, _) {
return initialState;
},
toggleStoryCreateModal(state, _) {
state.showStoryCreateModal = !state.showStoryCreateModal;
},
updateStories(state, { payload }) {
if (state.stories.length > 0) {
for (let i=0; i < state.stories.length; i++) {
const story = state.stories[i];
const filterededStoryArray = story.filter(item => item.author === payload.author);
if (filterededStoryArray.length > 0) {
filterededStoryArray.push(payload);
}
state.stories[i] = filterededStoryArray;
break;
}
} else {
state.stories = state.stories.unshift(...[payload]);
}
},
},
extraReducers: (builder) => {
builder.addCase(createStory.pending, (state, action) => {
state.isUploadingStory = true;
});
builder.addCase(createStory.fulfilled, (state, action) => {
state.isUploadingStory = false;
});
builder.addCase(createStory.rejected, (state, action) => {
state.isUploadingStory = false;
});
builder.addCase(getLoggedInUserStories.fulfilled, (state, action) => {
let stories =
action.payload.data.length > 0 ? [...action.payload.data] : [];
state.userStories = stories;
});
builder.addCase(getLoggedInUserStories.rejected, (state, action) => {});
builder.addCase(getFeedStories.fulfilled, (state, action) => {
let stories = action.payload.data.filter(
(userStory) => userStory.length > 0
);
state.stories = stories;
});
builder.addCase(deleteStory.pending, (state, action) => {
state.isDeletingStory = true;
});
builder.addCase(deleteStory.fulfilled, (state, action) => {
state.isDeletingStory = false;
});
builder.addCase(deleteStory.rejected, (state, action) => {
state.isDeletingStory = false;
});
},
});
export const selectStoryUploading = (state) => state.stories.isUploadingStory;
export const selectUserStories = (state) => state.stories.userStories;
export const selectFeedStories = (state) => state.stories.stories;
export const selectIsDeletingStory = (state) => state.stories.isDeletingStory;
export const selectShowStoryCreateModal = (state) =>
state.stories.showStoryCreateModal;
export const { clearStories, toggleStoryCreateModal, updateStories } = storiesSlice.actions;
export default storiesSlice.reducer;
The updateStories function is where I am having a challenge. Please what is the best way to go about this? The data I use to update this state comes from a web socket. The useEffect handles the updating of the state with the updateStory function.
useEffect(() => {
postSocket.connect();
storySocket.connect();
postSocket.on("post update", (data) => {
setNewPostLoading(true);
setTimeout(() => {
setNewPostLoading(false);
},1000);
setTimeout(() => {
dispatch(addPostToFeed(JSON.parse(data)));
},1000);
});
postSocket.on("single post update", data => {
dispatch(updatePostFeed(JSON.parse(data)));
});
storySocket.on("new story", data => {
console.log(data);
dispatch(updateStories(JSON.parse(data)));
});
postSocket.on("delete post", data => {
dispatch(removePostFromFeed(JSON.parse(data)));
});
},[dispatch, posts]);
I need to update the stories array in the state but the method I tried above is not working.
2
Answers
I was able to solve the challenge by making the change below to my code. Thank you
Thank you Linda for your support.
unshift()
The part of the code that is using
unshift()
is the right general approach and you should work with that. There is a problem though: theunshift()
method returns the length of the array, not the array itself. So this line won’t work, because you are settingstate.stories
to a number.unshift()
mutates the array, which is fine in an RTK reducer. You don’t need to assign or return anything. Just mutate the array.state.stories
is an array of arrays, so we need to find which array we want to modify and then we can callunshift
on it.It might be easier to work with if your
state.stories
was a dictionary where eachauthor
is a key and value for that key is the stories by that author.