Как правильно прописать extraReducer в постах, который следит за добавлением или удалением лайка

Рейтинг: 0Ответов: 1Опубликовано: 09.06.2023

Есть такой массив постов, пытаюсь прописать экстраредюсер, следящий за лайками, но никак не получается сделать это правильно. Всё, чего смог достичь это либо при лайке перезагружается весь компонент с постами, либо (текущий вариант) при лайке визуально увеличивается количество лайков на всех постах, работает 2 клика подряд потом ничего не меняет.

На беке на данный момент одна функция, которая проверяет,существует ли лайк или нет, и создаёт его или удаляет в зависимости от проверки. Лайки связаны с постами и юзерами через связь в секвалайзе belongsToMany

https://i.sstatic.net/rln66.png

likeslice


const initialState = {
  likes: []
}

export const upvotePost = createAsyncThunk(
  'likes/upvotePost',
  async (id, { rejectWithValue, dispatch }) => {
    try {
      const response = await axios.post(`/posts/${id}/likes`);
      dispatch(addLike(response.data.like))
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

// export const unlikePost = createAsyncThunk(
//   'likes/unlikePost',
//   async (id, { rejectWithValue, dispatch }) => {
//     try {
//       await axios.delete(`/posts/${id}/likes`);
//       dispatch(unlike(id))
//     } catch (error) {
//       rejectWithValue(error.message);
//     }
//   }
// )

export const getPostLikes = createAsyncThunk(
  'likes/getPostLikes',
  async(id, {rejectWithValue, dispatch}) => {
    try {
      const response = await axios(`/posts/${id}/likes`);
      dispatch(getLikes(response.data.postLikes));
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

export const likeSlice = createSlice({
  name: 'likes',
  initialState,
  reducers: {
    addLike: (state, action) => {
      state.likes.push(action.payload);
    },
    // unlike: (state, action) => {
    //   state.likes = state.likes.filter(el => el.id !== action.payload)
    // },
    getLikes: (state, action) => {
      state.likes = action.payload;
    }
  }
})

export const { addLike, unlike, getLikes } = likeSlice.actions;
export default likeSlice.reducer;

postSlice

const initialState = {
  posts: [],
  post_id: {}
}

export const addNewPost = createAsyncThunk(
  'posts/addNewPost',
  async (value, { rejectWithValue, dispatch }) => {
    try {
      const response = await axios.post('/posts', value);
      dispatch(addPost(response.data.newPost));
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

export const getAllPosts = createAsyncThunk(
  'posts/getAllPosts',
  async (_, { rejectWithValue, dispatch }) => {
    try {
      const response = await axios('/posts');
      dispatch(getPosts(response.data.allPosts));
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

export const removePost = createAsyncThunk(
  'posts/removePost',
  async (id, { rejectWithValue, dispatch }) => {
    try {
      await axios.delete(`/posts/${id}`);
      dispatch(deletePost(id));
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

export const getOnePost = createAsyncThunk(
  'posts/getOnePost',
  async (id, { rejectWithValue, dispatch }) => {
    try {
      const response = await axios(`/posts/${id}`);
      const result = {
        ...response.data.onePost,
        userId: response.data.onePost.User.id,
        userName: response.data.onePost.User.name,
        userEmail: response.data.onePost.User.email,
        userInfo: response.data.onePost.User.info,
        userAvatar: response.data.onePost.User.avatar
      }
      dispatch(getPostById(result));
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

export const postSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    addPost: (state, action) => {
      state.posts.push(action.payload);
    },
    getPosts: (state, action) => {
      state.posts = action.payload;
    },
    deletePost: (state, action) => {
      state.posts = state.posts.filter(el => el.id !== action.payload);
    },
    getPostById: (state, action) => {
      state.post_id = action.payload
    }
  },
  extraReducers: (builder) => {
    builder.addCase(likeSlice.actions.addLike, (state, action) => {
      console.log('extra reducer LIKE payload', action.payload);
      state.posts = state.posts.map(post => {
        if (post.likedBy.map(user => user.Likes !== action.payload)) {
          post.likedBy.push(action.payload)
          return {...post }
        } else {
          return post
        }
      })
    })
  //   builder.addCase(likeSlice.actions.unlike, (state, action) => {
  //     console.log('extra reducer UNLIKE', 'payload', action.payload);
  //     state.post_id = action.payload 
  //     state.posts = state.posts.filter(el => el.id !== action.payload)
  //   })
  },
})

const { addPost, getPosts, deletePost, getPostById } = postSlice.actions;
export default postSlice.reducer;

payload выглядит так

extra reducer LIKE payload 

{post_id: 51, user_id: 10, updatedAt: '2023-06-09T12:43:25.871Z', createdAt: '2023-06-09T12:43:25.871Z'}

Ответы

▲ 0Принят

На основе предоставленного кода, я вижу, что ты пытаешься обновить состояние posts в extraReducers раздела postSlice. Однако в твоем коде не реализована логика удаления лайка. Для правильного обновления состояния likedBy в постах тебе нужно реализовать обработку и добавление/удаление лайков в extraReducers. Вот как это можно сделать:

import { likeSlice } from './likeSlice';
//...

export const postSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    //...
  },
  extraReducers: (builder) => {
    builder
      .addCase(likeSlice.actions.addLike, (state, action) => {
        const { post_id, user_id } = action.payload;
        state.posts = state.posts.map(post => {
          if (post.id === post_id) {
            // Проверяем, есть ли уже лайк от пользователя
            const existingLike = post.likedBy.find(like => like.user_id === user_id);
            if (!existingLike) {
              post.likedBy.push(action.payload);
            }
          }
          return post;
        });
      })
      .addCase(likeSlice.actions.unlike, (state, action) => {
        const { post_id, user_id } = action.payload;
        state.posts = state.posts.map(post => {
          if (post.id === post_id) {
            // Удаляем лайк пользователя
            post.likedBy = post.likedBy.filter(like => like.user_id !== user_id);
          }
          return post;
        });
      });
  },
});

//...

В этом коде, при вызове likeSlice.actions.addLike, мы обновляем массив likedBy в соответствующем посте только в том случае, если лайк от данного пользователя еще не существует. Аналогично, при вызове likeSlice.actions.unlike, мы удаляем лайк пользователя из массива likedBy в соответствующем посте.

Обрати внимание, что тебе также понадобится раскомментировать соответствующий extraReducers для действия unlike в likeSlice.

Теперь, при вызове likeSlice.actions.addLike или likeSlice.actions.unlike, состояние posts должно обновляться корректно, и ты сможешь отслеживать и добавлять/удалять лайки на соответствующих постах.