Sole
Sole

Reputation: 3340

Saving to LocalStorage Array not updating correctly React

I am getting data from a Rest api, then I have a click event which the user can save an article. When saving the objects gets added to an empty array saved[]. Within the save() method, it should save to localStorage, but for some reason the method sometimes overwrites the array in localStorage and clears it. I have tried the following so far:

import React, { Component } from 'react';
import './news-hero.css';
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";

const responsive = {
    superLargeDesktop: {
        breakpoint: { max: 4000, min: 3000 },
        items: 1,
    },
    desktop: {
        breakpoint: { max: 3000, min: 1024 },
        items: 1,
    },
    tablet: {
        breakpoint: { max: 1024, min: 464 },
        items: 1,
    },
    mobile: {
        breakpoint: { max: 464, min: 0 },
        items: 1,
    },
};

class NewsHero extends Component {
    state = {
        loading: false,
        data: [],
        headline: [],
        saved: []
    }

    saved = headline => {
        this.setState(
            prevState => ({ saved: [...prevState.saved, headline] }),
            state => console.log('Saved articles = ', this.state.saved), 
            alert('Article saved'),
            localStorage.setItem('saved', JSON.stringify(this.state.saved)),
            localStorage.getItem('saved')
        );
    }

    constructor(props) {
        super(props)
        this.saved = this.saved.bind(this)
    }

    onError() {
        this.setState({
            imageUrl: "../assets/img-error.jpg"
        })
    }

    componentDidMount() {
        this.setState({ loading: true })
        fetch('https://newsapi.org/v2/everything?q=timbaland&domains=rollingstone.com,billboard.com&excludeDomains=townsquare.media&apiKey=8')
            .then(headline => headline.json())
            .then(headline => this.setState({ headline: headline.articles, loading: false }, () => console.log(headline.articles)))
    }

    render() {

        return (
            <div className="hero">
                <h2 className="text-left">News</h2>

                {this.state.loading
                    ? "loading..."
                    : <div>
                        <Carousel
                            additionalTransfrom={0}
                            showDots={true}
                            arrows={true}
                            autoPlaySpeed={3000}
                            autoPlay={false}
                            centerMode={false}
                            className="carousel-hero"
                            containerClass="container-with-dots"
                            dotListClass="dots"
                            draggable
                            focusOnSelect={false}
                            infinite
                            itemClass="carousel-top"
                            keyBoardControl
                            minimumTouchDrag={80}
                            renderButtonGroupOutside={false}
                            renderDotsOutside
                            responsive={responsive}>
                            {this.state.headline.map((post, indx) => {
                                return (
                                    <div className="text-left mt-5" key={indx}>
                                        <img className="media-img card-img-top card-img-hero" src={post.urlToImage} alt="Alt text"></img>
                                        <div className="card-body container hero-text-body">
                                            <h1 className="card-title hero-title text-truncate">{post.title}</h1>
                                            <button className="btn-primary btn mt-2 mb-4" onClick={() => this.saved(post)}>Add this article</button>
                                            <p className="card-text">{post.description}</p>
                                            <a href={post.url} target="_blank" rel="noopener noreferrer">Read More</a>
                                        </div>
                                    </div>
                                )
                            })}
                            {this.state.saved.map((post, indx) => {
                                return (
                                    <div className="text-left mt-5" key={indx}>
                                        <img className="media-img card-img-top card-img-hero" src={post.urlToImage} alt="Alt text"></img>
                                        <div className="card-body container hero-text-body">
                                            <h1 className="card-title hero-title text-truncate">{post.title}</h1>
                                            <button className="btn-primary btn mt-2 mb-4" onClick={() => this.saved(post)}>Add this article</button>
                                            <p className="card-text">{post.description}</p>
                                            <a href={post.url} target="_blank" rel="noopener noreferrer">Read More</a>
                                        </div>
                                    </div>
                                )
                            })}
                        </Carousel>

                    </div>
                }
            </div>
        )
    }
}
export default NewsHero; 

I am sure I am missing a step in the LocalStorage process maybe?

Upvotes: 1

Views: 289

Answers (2)

SM Chinna
SM Chinna

Reputation: 341

setState is asynchronous

    this.setState(
      prevState => ({ saved: [...prevState.saved, headline] }, () => {
        console.log('Saved articles = ', this.state.saved), 
        alert('Article saved'),
        localStorage.setItem('saved', JSON.stringify(this.state.saved)),
        localStorage.getItem('saved')
      }));

Upvotes: 0

rrd
rrd

Reputation: 5957

I'm not sure you are using setState correctly. There are two arguments to it (see here), and here you seem to have 5:

saved = headline => {
    this.setState(
        prevState => ({ saved: [...prevState.saved, headline] }),
        state => console.log('Saved articles = ', this.state.saved), 
        alert('Article saved'),
        localStorage.setItem('saved', JSON.stringify(this.state.saved)),
        localStorage.getItem('saved')
    );
}

I think you should do this instead, with the second argument holding your storage items, console.log and so on:

saved = headline => {
  this.setState(
   (prevState) => ({ saved: [...prevState.saved, headline] }),
    () => {
      console.log('Saved articles = ', this.state.saved);
      alert('Article saved');
      localStorage.setItem('saved', JSON.stringify(this.state.saved));
      localStorage.getItem('saved');
    });
}

Upvotes: 2

Related Questions