Search code examples
reactjsredux-toolkit

Redux-toolkit create different slices for different tasks


I'm creating an app when I have 2 pages one page will show all the posts of all the users and the second post will show all posts of the logged in user.

Should I create two different slices for these 2 pages or just one is okay like I've done below?

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axiosInstance  from "../utils/axiosInstance";

export const fetchMemoryPosts = createAsyncThunk('posts/fetchPosts', async (_, thunkApi)=>{

  try {
    const response = await axiosInstance.get('/posts');
    return response.data.posts;
  } catch (error) {
    const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
    const errorCode = error.response.status;
    return thunkApi.rejectWithValue({errorMessage,errorCode});
  }
});

export const fetchPostDetails = createAsyncThunk('posts/fetchPostDetails', async (id, thunkApi)=>{
  try {
    const response = await axiosInstance.get(`/posts/post-details/${id}`);
    return response.data.data.post;
  } catch (error) {
    const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
    const errorCode = error.response.status;
    return thunkApi.rejectWithValue({errorMessage,errorCode});
  }
});

export const createMemoryPost = createAsyncThunk('posts/createPosts', async(postData, thunkApi)=>{

  const tagsArr = postData.tags.split(',').map((ele)=> ele.trim());
  const preparedData = {...postData, tags: tagsArr};

  try {
    const response = await axiosInstance.post('/posts/create', preparedData);
    return response.data.data;
  } catch (error) {
    const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
    const errorCode = error.response.status
    return thunkApi.rejectWithValue({errorMessage, errorCode})
  }
});

export const patchMemoryPost = createAsyncThunk('posts/patchPosts', async(data, thunkApi)=>{
  const  {postId: id, postData} = data;
  const tagsArr = postData.tags.split(',').map((ele)=> ele.trim());
  const preparedData = {...postData, tags:tagsArr};

  try {
    const response = await axiosInstance.patch(`posts/patch/${id}`, preparedData);
    return response.data.data;
  } catch (error) {
    const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
    const errorCode = error.response.status
    return thunkApi.rejectWithValue({errorMessage, errorCode})
  }
});

export const deleteMemoryPost = createAsyncThunk('posts/deletePosts', async(data, thunkApi)=>{
  try {
    const id = data;
    const response = await axiosInstance.delete(`/posts/delete/${id}`);
    return response.data.data;
  } catch (error) {
    const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
    const errorCode = error.response.status
    return thunkApi.rejectWithValue({errorMessage, errorCode})
  }
});

export const getUserMemoryPosts = createAsyncThunk("getUserMemoryPosts", async(_, thunkApi)=>{
  try {
    const response = await axiosInstance.get('posts/my-posts');
    return response.data.data.posts;
  } catch (error) {
    const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
    const errorCode = error.response.status
    return thunkApi.rejectWithValue({errorMessage, errorCode})
  }
})

const initialState = {
  posts: [],
  userPosts: [],
  detailedPost: {},
  status: 'idle',
  error: null,
  toEditPostId: null,
};

const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    setToEditPostId(state,action){
      state.toEditPostId = action.payload;
    },
    clearUserPostsFromMemory(state, _){
      state.userPosts = [];
    },
    clearDetailedPostData(state, _){
      console.log("cleared");
      state.detailedPost = {};
    }
  },
  extraReducers(builder){
    builder
    .addCase(fetchMemoryPosts.pending, (state,_)=>{
      state.status = 'loading';
    })
    .addCase(fetchMemoryPosts.fulfilled, (state, action)=>{
      state.status = 'success';
      state.posts = action.payload;
    })
    .addCase(fetchMemoryPosts.rejected, (state, action)=>{
      state.status = 'failure';
      state.error = action.payload;
    })
    .addCase(createMemoryPost.fulfilled, (state, action)=>{
      state.status = 'success';
      state.userPosts = state.userPosts.concat(action.payload);
      state.posts = state.posts.concat(action.payload);
    })
    .addCase(createMemoryPost.rejected, (state, action)=>{
      state.status = 'failure';
      state.error = action.payload;
    })
    .addCase(patchMemoryPost.fulfilled, (state, action)=>{
      const {_id} = action.payload;
      state.userPosts = state.userPosts.map((post)=>{
        if(post._id === _id){
          return action.payload;
        }
        return post;
      });

      state.posts = state.posts.map((post)=>{
        if(post._id === _id){
          return action.payload;
        }
        return post;
      });
    })
    .addCase(patchMemoryPost.rejected, (state, action)=>{
      state.error = action.payload;
    })
    .addCase(deleteMemoryPost.fulfilled, (state, action)=>{
      const {_id} = action.payload
      state.status = 'success';
      state.userPosts = state.userPosts.filter((post)=> post._id !== _id);
      state.posts = state.posts.filter((post)=> post._id !== _id);
    })
    .addCase(deleteMemoryPost.rejected, (state, action)=>{
      state.status = 'failure';
      state.error = action.payload;
    })
    .addCase(getUserMemoryPosts.pending, (state,_)=>{
      state.status = 'loading';
    })
    .addCase(getUserMemoryPosts.fulfilled, (state, action)=>{
      state.status = 'success';
      state.userPosts = action.payload;
    })
    .addCase(getUserMemoryPosts.rejected, (state, action)=>{
      state.status = 'failure';
      state.error = action.payload;
    })
    .addCase(fetchPostDetails.pending, (state, _)=>{
      state.status = "loading";
    })
    .addCase(fetchPostDetails.fulfilled, (state, action)=>{
      state.status = "success";
      // console.log(action.payload);
      state.detailedPost = action.payload;
    })
    .addCase(fetchPostDetails.rejected, (state, action)=>{
      state.error = "failure"
      state.error = action.payload;
    })
  }
});

export const getMyPosts = (state) => state.posts.userPosts;
export const getAllPostsSelector = (state) => state.posts.posts;
export const getPostsErrorSelector = (state) => state.posts.error;
export const getPostsStatusSelector = (state) => state.posts.status;
export const getToEditPostIdSelector = (state) => state.posts.toEditPostId;

export const { setToEditPostId, clearUserPostsFromMemory, clearDetailedPostData } = postsSlice.actions;

export default postsSlice.reducer;

If its okay to use just one slice, then should I use 2 error and 2 loading states in the initial state one error & loading state for all user posts and one error an loading for the logged in user?


Solution

  • I don't see any issues with your code i bet it'll work however, each slice should be written in diffrent files, for your case if you're using react you'll have bugs when you try to persist your state on page refresh because redux clears the current state of the application on page refresh. Creating diffrent slices makes it easier to manage then and making changes also when it comes to persisting the state you'll be able to specify which reducers to persist .