Ars
Ars

Reputation: 44

React and localstorage

I'm making a blog with posts that can be liked and saved to localstorage.

I get posts from api.

When you like, the color and the number of likes change. The function itself works. But when I like one post, after refreshing the page, all posts get likes. That is, localstorage does not work correctly, I think. For a long time I tried to fix it myself, but it turns out to be done. I hope for your help, friends.

Here is the code:

import React from 'react';
import { format } from 'date-fns';
import './BlogList.scss';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';

function BlogList({
  key,
  title,
  tagList,
  favoritesCount,
  createdAt,
  description,
  favorited,
  authorUserName,
  authorImage,
}) {
  const [likeCount, setLikeCount] = React.useState(() => {
    const saved = localStorage.getItem('likeCount');
    const initialValue = JSON.parse(saved);
    return initialValue || favoritesCount;
  });
  const [liked, setLiked] = React.useState(() => {
    const saved = localStorage.getItem('liked');
    const initialValue = JSON.parse(saved);
    return initialValue || favorited;
  });

  const likePost = () => {
    setLiked(!liked);
    // eslint-disable-next-line no-unused-expressions
    setLikeCount(liked ? likeCount - 1 : likeCount + 1);
  }
  React.useEffect(() => {
    localStorage.setItem('likeCount', JSON.stringify(likeCount));
    localStorage.setItem('liked', JSON.stringify(liked))
  });
  const heartFill = liked ? 'crimson' : 'currentColor';
  const createDate = format(new Date(createdAt), 'MMMM d, uu');
  return (
    <div key={key} className="blog-frame">
      <div className="blog-frame__content">
        <div className="blog-article">
          <h5>{title}</h5>
          <button type="button" className="like-btn" onClick={likePost}>
            <FavoriteBorderIcon style={{ fill: heartFill }} />
          </button>
          <p className="like-count">
            {likeCount}
          </p>
        </div>
        {tagList.map((tag, idx) => {
          return <button key={`${tag}-${idx}`} type="button" className="btn-blog-tag">{tag}</button>
        })}
        <p className="blog-frame__text">
          {description}
        </p>
      </div>
      <div className="blog-frame__profile">
        <div className="profile-article">
          <h6>{authorUserName}</h6>
          <p className="profile-date">{createDate}</p>
        </div>
        <div className="profile-avatar"><img src={authorImage} alt="avatar" /></div>
      </div>
    </div>
  )
}

export default BlogList;

I liked one post:enter image description here

After refreshing the page: enter image description here

My BlogContent component where my props are located

import React from 'react';
import BlogList from '../BlogList/BlogList';
import useFetchData from '../../services/useFetchData';
import './BlogContent.scss';

function BlogContent() {
  const { posts } = useFetchData();

  const dataposts = posts.map((post, index) => {
    return (
      // eslint-disable-next-line react/no-array-index-key
      <React.Fragment key={index}>
        <BlogList
          post={post}
          title={post.title}
          tagList={post.tagList}
          favoritesCount={post.favoritesCount}
          createdAt={post.createdAt}
          description={post.description}
          favorited={post.favorited}
          authorUserName={post.author.username}
          authorImage={post.author.image}
        />
      </React.Fragment>
    );
  });

  return (
    <div className="blog-list-wrapper">
      {dataposts}
    </div>
  );
}

export default BlogContent;

I get posts from useFetchData:

import React from 'react';
import axios from 'axios';

const useFetchData = () => {
  const [posts, setPosts] = React.useState([]);

  React.useEffect(() => {
    const fetchData = async () => {
      try {
        const { data: response } = await axios.get('http://kata.academy:8022/api/articles');
        setPosts(response.articles);
      } catch (error) {
        console.error(error);
      }
    };

    fetchData();
  }, []);
  return {
    posts,
  };
};

export default useFetchData;

I apologize for such details. Made everything clear to you. Thanks.

Upvotes: 0

Views: 620

Answers (2)

zain ul din
zain ul din

Reputation: 619

Pass [ likeCount , liked ] to dependence list of useEffect.By this every time the state changes it'll update the value in local storage. Load local storage only once whenever the page is rendered. Have a look at this code example

const [likeCount, setLikeCount] = React.useState('')
const [liked , setLiked] = React.useState('') 
   
 useEffect(()=>{ 
   // calls only once
   setLikeCount('localStorageVal')
   setLiked ('localStorageVal')
 }, [ ] )

 useEffect(()=>{ 
    // saves to local storage
 }, [ liked , likeCount ] )

Upvotes: 1

Tushar
Tushar

Reputation: 1306

You have to store a pair of like counts and their respective ID. Right now you are just saving the like count and repopulating it in UI. Your code has no logic of how to determine which likes are for which posts.

// Try something like this
const mapoflikes = {};
// increase mapofLikes
mapofLikes[id] = mapofLikes[id]+1 ?? 1
// save this to localstorage
// retrieve this from localstorage.

Upvotes: 1

Related Questions