0

I am building post like / unlike feature using React context, but I have no idea what to do in reducer to update UI. Currently when I click like / unlike button, ui doesn't update instantly, have to refresh page to see the update.

backend logic

exports.likePost = async (req, res) => {
  try {
    const result = await Post.findByIdAndUpdate(
      req.body.postId,
      {
        $push: { likes: req.body.userId },
      },
      { new: true }
    );

    return res.json(result);
  } catch (err) {
    console.log(err.message);
  }
};

exports.unlikePost = async (req, res) => {
  try {
    const result = await Post.findByIdAndUpdate(
      req.body.postId,
      {
        $pull: { likes: req.body.userId },
      },
      { new: true }
    );

    return res.json(result);
  } catch (err) {
    console.log(err.message);
  }
};

component

 {post.likes.includes(loggedInUser._id) ? (
            <IconButton
              color="secondary"
              component="span"
              onClick={() => unlikePost(loggedInUser._id, post._id)}
            >
              <Like />
            </IconButton>
          ) : (
            <IconButton
              color="secondary"
              component="span"
              onClick={() => likePost(loggedInUser._id, post._id)}
            >
              <Unlike />
            </IconButton>
          )}

context

const initialState = {
  posts: [],
};

// Like post
  const likePost = async (userId, postId) => {
    try {
      const res = await axios.put(
        `/api/posts/like`,
        { userId, postId },
        {
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${localStorage.getItem("token")}`,
          },
        }
      );
      dispatch({ type: "LIKE_POST", payload: res.data });
    } catch (err) {
      console.log(err);
    }
  };

  // Unlike post
  const unlikePost = async (userId, postId) => {
    try {
      const res = await axios.put(
        `/api/posts/unlike`,
        { userId, postId },
        {
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${localStorage.getItem("token")}`,
          },
        }
      );
      dispatch({ type: "UNLIKE_POST", payload: res.data });
    } catch (err) {
      console.log(err);
    }
  };

reducer

case "LIKE_POST":
      return {
        ...state,
        posts: // ???
        ),
      };
case "UNLIKE_POST":
      return {
        ...state,
        posts: // ???,
      };

What should be the logic for reducer?

Farhan Farooq
  • 193
  • 2
  • 13

1 Answers1

0

Something like this:

case "LIKE_POST":
  return {
    ...state,
    like: action.likeValue,
  };

case "UNLIKE_POST":
  return {
    ...state,
    unlike: action.unlikeValue,
  };

When you want to change the value:

dispatch({ type: "LIKE_POST", likeValue: res.data });
dispatch({ type: "UNLIKE_POST", unlikeValue: res.data });

In your initial state:

const initialState = {
  posts: [],
  like: [],
  unlike: [],
};

Here is a good explanation, which helped me: link

Amel
  • 653
  • 9
  • 15
  • It worked. Thank you. Lets say I will add comments feature too. So do I have to define in initialState as an empty array too? Is it ok to add like, unlike, comments and so on in state? – Farhan Farooq Aug 31 '20 at 18:58
  • I am glad it worked. If you need a variable (for example comments) only in one component, then you can add it in the state of the component. If you need it in child / parent component, then you can pass it with props or you can define it in initial state. If you need it in another component then define it in initial state and set it with reducer. That way the context API makes that variable available in every component you need. You can define as many variables in Initial State as you want and they can be from every type you want (Array, Object, String, Number, ...). – Amel Sep 01 '20 at 10:35