0

I have a saved[] array in my component. I want to pass this data from one component to another.

I have tried the following:

<Home default path='/news' savedArticles={this.state.saved}/>

But this throws an error as it says TypeError: Cannot read property 'saved' of null

So this is the component which has they data.

NewsMain.js

import React, { Component } from 'react';
import '../news-main/news-main.css';
import News from '../news/news';
import NewsHero from '../news-hero/news-hero';
import Sports from '../sports/sports';

class NewsMain extends Component {
    render() {
        return (
            <div>
                <NewsHero />
                <News />
                <Sports />
            </div>
        )
    }
}
export default NewsMain;

The data comes from the NewsHero /> component.

news.hero.js

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] }),
          () => {
            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, saved: localStorage.getItem('saved') ? JSON.parse(localStorage.getItem('saved')) : [] })
        fetch('https://newsapi.org/v2/everything?q=timbaland&domains=rollingstone.com,billboard.com&excludeDomains=townsquare.media&apiKey=8ee8c21b20d24b37856fc3ab1e22a1e5')
            .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>
                                )
                            })}
                        </Carousel>
                    </div>
                }
            </div>
        )
    }
}
export default NewsHero;

I want to pass the saved array to other components e.g.

app.js

import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
import Header from './header/header';
import NewsMain from './news-main/news-main';
import Footer from './footer/footer';
import Home from './home/home';
import { Router } from '@reach/router';

class App extends Component {

  render() {
    return (
      <div className="App">
        <Header />
        <div>
          <Router>
            <Home default path='/news' savedArticles={this.state.saved} />
            <NewsMain path='/news' />
          </Router>
        </div>
        <Footer title="Footer" />
      </div>
    )
  }
}
export default App;

Any idea's?

2 Answers 2

1

You have not initiated your state anywhere in your app, so this.state is undefined.

Your app.js should look like this:

import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
import Header from './header/header';
import NewsMain from './news-main/news-main';
import Footer from './footer/footer';
import Home from './home/home';
import { Router } from '@reach/router';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {saved: []};
  }
  render() {
    return (
      <div className="App">
        <Header />
        <div>
          <Router>
            <Home default path='/news' savedArticles={this.state.saved} />
            <NewsMain path='/news' />
          </Router>
        </div>
        <Footer title="Footer" />
      </div>
    )
  }
}
export default App;

Take a look here https://it.reactjs.org/docs/state-and-lifecycle.html for more info on state and lifecycle in reactjs.

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

Comments

1

In your code, you are passing property "saved" from object "state" to Home before you are declaring any state.

this.state in render method of your App componend is state only of your App component. I believe that you would like to use property from state of NewsHero component. This property is only accessible within NewsHero component and you have to somehow pass it up to App component and only then you can pass it down toward Home.

You can also use some global state or some other state library like Redux, but clearly you have to refractor given code

Comments

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.