0

I receive JSON from my back end, I save it in my state and I want to use it in props in another react component, but it doesn't work.

I try to show need date like that in props of my component - {this.state.movies["0"]["title"]}, but it doesn't work.

  constructor() {
    super();
    this.state = {
      movies: []
    }
  }

  componentDidMount() {
    this.getAllMoviesForMainPage();
  }

  getAllMoviesForMainPage() {
    axios.get("http://localhost:8080/showAll")
      .then(response => {
        this.setState({ movies: response.data })
      })
  }

  render() {
    return (
      <div className="qwerty">
        <MainPageComponent />
        <div className='moviePreviewGrid'>
          <Router>
            <div className="moviePreviewGrid-row">
              <div className="moviePreviewGrid-col">
                <MoviePreview
                  Title={this.state.movies["2"]["title"]}
                  MoviePreviewAvatar={DrivePoster}
                  SeeMore="unrelated long string here"
                />
                <NavLink to="/showByTitle/Драйв">
                  <button type="button" className="myBtn">See more</button>
                </NavLink>
              </div>

and structure of my JSON

[
    {
        "id": 1,
        "title": "Джокер",
        "releaseDate": "2019",
        "genre": "Триллер, драма, криминал",
        "duration": "122 минуты",
        "rating": 8.7,
        "criticReviews": [
            {
                "criticName": "Anton",
                "review": "anton review"
            },
            {
                "criticName": "OldCritic",
                "review": "old review"
            }
        ],
        "userReviews": [
            {
                "nickName": "Igor",
                "review": "igor review"
            },
            {
                "nickName": "Nik",
                "review": "nik review"
            }
        ]
    },
    {
        "id": 2,
        "title": "Ирландец",
        "releaseDate": "2019",
        "genre": "Драма, триллер, криминал, биографический",
        "duration": "209 минут",
        "rating": 8.4,
        "criticReviews": [
            {
                "criticName": "YoungCritic",
                "review": "young review"
            }
        ],
        "userReviews": [
            {
                "nickName": "Gambit",
                "review": "gambit review"
            },
            {
                "nickName": "Andrew",
                "review": "andrew review"
            }
        ]
    },
    {
        "id": 3,
        "title": "Драйв",
        "releaseDate": "2011",
        "genre": "Боевик, драма",
        "duration": "100 минут",
        "rating": 7.8,
        "criticReviews": [
            {
                "criticName": "Critic",
                "review": "review"
            }
        ],
        "userReviews": [
            {
                "nickName": "Anton",
                "review": "anton review"
            }
        ]
    },
    {
        "id": 4,
        "title": "Последний человек на Земле",
        "releaseDate": "2015",
        "genre": "Комедия",
        "duration": "22 минуты",
        "rating": 7.3,
        "criticReviews": [
            {
                "criticName": "NewCritic",
                "review": "new review"
            }
        ],
        "userReviews": [
            {
                "nickName": "Atomf7",
                "review": "atomf7 review"
            }
        ]
    },
    {
        "id": 5,
        "title": "Интерстеллар",
        "releaseDate": "2014",
        "genre": "Фантастика, драма, приключения",
        "duration": "169 минут",
        "rating": 8.6,
        "criticReviews": [
            {
                "criticName": "Nik",
                "review": "nik review"
            }
        ],
        "userReviews": [
            {
                "nickName": "Alice",
                "review": "alice review"
            }
        ]
    }
]

and i wont to have for example title of first movie

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
qwerty
  • 123
  • 9

3 Answers3

2

In the first init, the this.state.movies has a length of 0 so this.state.movies["2"]["title"] of course would have no value at all because getAllMoviesForMainPage is async (axios call) and takes a little longer to complete, so first you must give it an initial value and only when the request completes you can give it the real value.

example:

<MoviePreview
  Title={this.state.movies.length > 0 ? this.state.movies[2].title : ""}
  MoviePreviewAvatar={DrivePoster}
/>

Usually a state, isLoading is used for this case. So you can know when you received your data.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Renaldo Balaj
  • 2,390
  • 11
  • 23
  • i'm tried it, but it still don't work with this error: Uncaught TypeError: Cannot read property 'title' of undefined in console – qwerty Dec 09 '19 at 16:20
  • 1
    @qwerty the condition was initially wrong in this answer, I fixed it so now it should work (unless you receive less than 3 movies). – Emile Bergeron Dec 09 '19 at 16:36
1

You are making a GET request in componentDidMount which is async so until the promise is resolve in .then(.. your state does not contain response.data in movies.

The important thing to remember is component gets re-render every time you do this.setState(.. , so before you do setState in your getAllMoviesForMainPage method, the initial render will happen and it will contain an empty array in this.state.movies that you have defined in constructor

Also since your response contains array of objects, you would be using movies['2'].title instead of movies['2']['title']

So your render method should contain something like this to show movie preview:

    <div className="moviePreviewGrid-col">
      {this.state.movies.length ? (
        <MoviePreview
          Title={this.state.movies['2'].title}
          MoviePreviewAvatar={DrivePoster}
          SeeMore="unrelated long string here"
        />
      ) : (
        <PlaceHolderPreview />
      )}
      <NavLink to="/showByTitle/Драйв">
        <button type="button" className="myBtn">
          See more
        </button>
      </NavLink>
    </div>

PlaceHolderPreview can be your another component that will show -- well a placeholder for preview.

Hope it all makes sense.

Haris Mehmood
  • 854
  • 4
  • 15
  • 26
  • _"`movies['2'].title` instead of `movies['2']['title']`"_ both are [equally valid](https://stackoverflow.com/q/4968406/1218980). The best practice though would be to use a number for the index within the square brackets: `movies[2].title`. – Emile Bergeron Dec 09 '19 at 16:49
1

In the first render movies are not already fetched from the api.

So you need to conditionally render it.

import React, { Component } from "react";
import axios from "axios";

export default class Test extends Component {
  constructor() {
    super();
    this.state = {
      movies: [],
      loading: true
    }
  }

  componentDidMount() {
    this.getAllMoviesForMainPage();
  }

  getAllMoviesForMainPage() {
    axios.get("http://localhost:8080/showAll")
      .then(response => {
        this.setState({ movies: response.data, loading: false })
      })
  }

  render() {
    const { loading, movies } = this.state;

    if (loading) {
      return <div>Loading...</div>;
    } else {
      return (
        <div>
          {movies.length > 0 ? (
            <div>First movie title: {movies[0].title}</div>
          ) : (
            <div>No movies</div>
          )}
        </div>
      );
    }
  }
}

You can check this example using a fake api.

SuleymanSah
  • 17,153
  • 5
  • 33
  • 54