Search code examples
reactjsreduxreact-reduxredux-toolkit

How can I add an object to an array withing another array in redux tool kit state


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.


Solution

  • I was able to solve the challenge by making the change below to my code. Thank you

    updateStories(state, { payload }) {
          const newArray = [];
          if (state.stories.length > 0) {
            for (let i=0; i < state.stories.length; i++) {
              const story = state.stories[i];
              const filteredStoryArray = story.filter(item => item.author === payload.author);
              if (filteredStoryArray.length > 0) {
                state.stories = state.stories.filter((story, index) => index !== i);
                newArray.push(...story);
              }
              break;
            }
          }
          if (newArray.filter(item => item._id === payload._id).length === 0) {
            newArray.push(payload)
          };
          state.stories.push(newArray);
        },
      },
    

    Thank you Linda for your support.