1

I am trying to push a object from a JSON response from a rest api into an empty array in React, so far my code is not working, I am fairly new to React so can't see where I am going wrong? Maybe something to do with the function in the state? I am getting error:

Cannot read property 'saved' of undefined

code 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() {
        this.saved.push(this.headline);
        //alert('this is saved kind of');
    }

    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}>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;

Any idea's and insight?

1

2 Answers 2

5

Cannot read property 'saved' of undefined

You are not correctly referencing this.state for saved or headline.

Cannot read property 'state' of undefined

Either add a constructor and bind this to your saved function.

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

saved() {
    this.saved.push(this.headline);
    //alert('this is saved kind of');
}

Or define saved as an arrow function to bind this

saved = () => {
    this.saved.push(this.headline);
    //alert('this is saved kind of');
}

Should be

saved() {
  const { headline, saved } = this.state;
  this.setState({ saved: [...saved, headline] });
}

or

saved = () => {
  const { headline, saved } = this.state;
  this.setState({ saved: [...saved, headline] });
}

UPDATE: Save a specific post/headline

I see now, if you wish to save a specific headline then you need to update the signature of your saved function and how you call it.

saved = headline => {
  this.setState(
    prevState => ({ saved: [...prevState.saved, headline] })
  );
}

and when you invoke it as a callback

<button
  className="btn-primary btn mt-2 mb-4"
  onClick={() => this.saved(post)} // pass the current post defined in the map callback
>
  Add this article
</button>

One minor comment about naming, consistently referencing the same "object" the same way throughout code goes a long way in helping readability. I.E. how you reference headlines, posts, and articles. Pick one and be consistent.

this.state.headlines => this.state.headlines.map(headline => ...

this.state.posts => this.state.posts.map(post => ...

this.state.articles => this.state.articles.map(article => ...

Sign up to request clarification or add additional context in comments.

10 Comments

getting an error with that? Parsing error: Unexpected token, expected ","
was missing a }
But even with that code Drew, getting the same error - Cannot read property 'state' of undefined
My next guess then is that the saved function isn't correctly bound to this. Updating with binding suggestions.
Thanks that did work in a way, pushing to the empty array but when I console log it, It is pushing nothing on the first click of the saved() function but when I click save() again, it pushes all of the objects into the array not just the article I want, could this be something to do with the key index in the render() method?
|
2
    saved() {
        this.saved.push(this.headline);
        //alert('this is saved kind of');
    }

You cannot defined class methods like this without writing a constructor like this:

class NewsHero extends Component {
  constructor(props){
    super(props)
    this.saved = this.saved.bind(this)

  }

...the rest of everything you wrote

}

Without doing this, your saved function is not bound to the instance of the class. Its silly, yes, but that's how class methods work in javasript. The other option is to use an arrow function, which preserves the context if this:

    saved = () => {
        this.saved.push(this.headline);
        //alert('this is saved kind of');
    }

I think this is faster and cleaner, and its pretty commonly used.

That is problem 1. Problem 2 is that it appears you are trying to modify the state object directly when writing this.saved.push(this.headline). That's not how to do it. To modify your state, you need to use the this.setState function:

saved = () => {
  this.setState({
    saved: whatever you want this to be
  })
}

But it looks like you are already setting your state correctly in your fetch call, so I'm not sure what you're trying to accomplish with your saved function...?

1 Comment

So the saved() function should add that specific article to the saved array, which are JSON objects in the rest api call.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.