Home > Mobile >  How to update a single post in an array of posts
How to update a single post in an array of posts

Time:07-17

How do I give a rating for a single post but not have to refetch all the posts.

Currently I have an array of posts that I map through. I am giving a rating for one post but have to refetch all the posts each time. Is there a way to only refetch the post I am giving the rating for?

On the Frontend I have:

const [posts, setPosts] = useState([])
     
useEffect(() => {
                  allPosts()
              }, [])
        
        const allPosts = async () => {
            try {
              
              const { data } = await axios.get(`/posts/${page}`)
              setPosts(data)
            } catch (err) {
              console.log(err)
            }
          }
    
          const likePost = async (e, newValue) => {
            e.preventDefault()
            setValue1(newValue)
            try {
              const { data } = await axios.put('/like-post', {
                likeId: currentPost._id,
                hover,
              })
            } catch (err) {
              console.log(err)
            }
          }

In the Backend I am collecting all the posts:

  export const posts = async (req, res) => {
  try {
    const currentPage = req.params.page || 1
    const perPage = 5
    const posts = await Post.find()
      .skip((currentPage - 1) * perPage)
      .limit(perPage)
      .populate('postedBy')
      .populate('comments.postedBy', '_id name image')
      .sort({ createdAt: -1 })
    res.json(posts)
  } catch (err) {
    console.log(err)
  }
}

To Like the Post on the Frontend I am:

     const [value1, setValue1] = useState(0)
    
          const likePost = async (e, newValue) => {
            e.preventDefault()
            setValue1(newValue)
            try {
              const { data } = await axios.put('/like-post', {
                likeId: currentPost._id,
                hover,
              })
            
            allPosts()
            } catch (err) {
              console.log(err)
            }
          }

And to like on the Backend:

export const likePost = async (req, res) => {
  try {
    const { likeId, hover } = req.body
    const user = await User.findById(req.user._id).exec()
    const post = await Post.findById(likeId).exec()
    // console.log(user.id)

    let existingRatingObject = post.likes.find((ele) => ele.postedBy == user.id)
    if (existingRatingObject === undefined) {
      let ratingAdded = await Post.findByIdAndUpdate(
        likeId,
        {
          $push: { likes: { starValue: hover, postedBy: user } },
        },
        { new: true },
      ).exec()
      res.json(ratingAdded)
    } else {
      // if user have already left rating, update it
      const ratingUpdated = await Post.updateOne(
        { likes: { $elemMatch: existingRatingObject } },
        { $set: { 'likes.$.starValue': hover } },
        { new: true },
      ).exec()
      res.json(ratingUpdated)
    }
  } catch (err) {
    console.log(err)
  }
}

CodePudding user response:

Since you are returning the updated post you could implement the update logic on your client to avoid refetching all data:

const likePost = async (e, newValue) => {
  e.preventDefault();
  setValue1(newValue);
  try {
    const { data } = await axios.put('/like-post', {
      likeId: currentPost._id,
      hover,
    });

    const postIndex = posts.indexOf((post) => post._id === data._id);
    if (postIndex === -1) {
      // Add new post
      setPosts((oldData) => {
        return [...oldData, data]
      })
    } else {
      // Update post
      setPosts((oldData) => {
        oldData[postIndex] = data
        return oldData
      })
    }
  } catch (err) {
    console.log(err);
  }
};

CodePudding user response:

Below is my JSX. I map posts to post and inside the post component I have a loadLikes, showAverage function and likePost.

{posts.map((post) => (
     <div key={post._id} className="mb-3">
       <Post
           post={post}
        />`

Then I have the following functions:

export default function post({
  post,
}) {

  const [state, setState] = useContext(UserContext)
  const [hover, setHover] = useState(-1)
  const [value1, setValue1] = useState(0)
  const [postAverage, setPostAverage] = useState(0)
     
//UE - show likes
      useEffect(() => {
        if (state && state.token) {
          loadLikes()
          showAverage()
        }
      }, [state && state.token])
    
      //V - Like Rating
    
      const loadLikes = async () => {
        console.log('load likes')
        if (post.likes && state.user) {
          let existingRatingObject = await post.likes.find(
            (ele) => ele.postedBy == state.user._id,
          )
          existingRatingObject && setValue1(existingRatingObject.starValue) // current user's star
        }
      }
    
      const showAverage = () => {
        console.log('show average run')
        if (post && post.likes && state.user) {
          let likesArray = post.likes
          let total = []
          let length = likesArray.length
          // console.log("length", length);
    
          likesArray.map((r) => total.push(r.starValue))
          let totalReduced = total.reduce((p, n) => p   n, 0)
          // console.log("totalReduced", totalReduced);
    
          let highest = length * 5
          // console.log("highest", highest);
    
          let average = (totalReduced * 5) / highest
          // console.log("result", result);
    
          setPostAverage(average)
          // console.log('POST AVERAGE', postAverage)
        }
      }
    
      const likePost = async (e, newValue) => {
        e.preventDefault()
        setValue1(newValue)
        setAnchorEl(null)
    
        try {
          const { data } = await axios.put('/like-post', {
            likeId: currentPost,
            hover,
          })
          socket.emit('new-post', hover)
    
          const postIndex = posts.indexOf(currentPost)
          const newRatedPost = data.ratingUpdatedDetails
          setPosts((oldData) => {
            oldData[postIndex] = newRatedPost
            return oldData
          })
    
        } catch (err) {
          console.log(err)
        }
      }
return (
    <>
<Rating
          size="large"
          name="hover-feedback"
          value={value1}
          precision={0.5}
          onChange={(e, newValue) => likePost(e, newValue)}
          onChangeActive={(event, newHover) => {
            setHover(newHover)
          }}
          onClick={() => handlePostID(post)}
     emptyIcon={<StarIcon style={{ opacity: 0.55 }}fontSize="inherit"/>}
        />
<>
)
}

I have added the oldData code in like you suggested, but this doesnt rerender the mapped post prop? not sure what I can do to update this?

@Ipizzinidev

  • Related