skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.


  2. 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: the unshift() method returns the length of the array, not the array itself. So this line won’t work, because you are setting state.stories to a number.

    state.stories = state.stories.unshift(...[payload]);
    

    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 call unshift on it.

    updateStories(state, { payload }) {
       // find the correct array
       const authorStories = state.stories.find(array =>
           array.some(item => item.author === payload.author)
       );
       // add to the existing array
       if (authorStories) {
           authorStories.unshift(payload);
       }
       // create new array if no matching array was found
       else {
           state.stories.unshift([payload]);
       }
    }
    

    It might be easier to work with if your state.stories was a dictionary where each author is a key and value for that key is the stories by that author.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search