On my Home component, on initial load, I loop through an object of URLs, and use Promise.all to make sure they all resolve at the same time. I construct an object, and push it into state. At the start of this I have loading: true, then set to false when done pushing that object into state:
Home Component:
class Home extends Component {
state = {
searchTerm: null,
movies: {
trending: {},
topRated: {},
nowPlaying: {},
upcoming: {}
},
loading: false
};
componentDidMount() {
this.getInitalMovies();
}
getInitalMovies = () => {
const API_KEY = process.env.REACT_APP_API_KEY;
//set loading to true
this.setState({ loading: true });
//create an object with all movie URLs
const allMovieURLs = {
trending: `https://api.themoviedb.org/3/trending/movie/day?api_key=${API_KEY}`,
topRated: `https://api.themoviedb.org/3/movie/top_rated?api_key=${API_KEY}&language=en-US&page=1`,
nowPlaying: `https://api.themoviedb.org/3/movie/now_playing?api_key=${API_KEY}&language=en-US&page=1`,
upcoming: `https://api.themoviedb.org/3/movie/upcoming?api_key=${API_KEY}&language=en-US&page=1`
};
//break down the movieURL object into entries, fetch the URL, and reassign entries with actual data
//encapsulate within a Promise.all to ensure they all resolve at the same time.
const moviePromises = Promise.all(
Object.entries(allMovieURLs).map(entry => {
const [key, url] = entry;
return fetch(url).then(res => res.json().then(data => [key, data]));
})
);
//with the returned promise from Promise.all, reconstruct the array of entries back into an object with relevant key pair values
const movies = moviePromises.then(movieArr => {
const dataObj = {};
for (const [movie, movieData] of movieArr) {
dataObj[movie] = movieData;
}
return dataObj;
});
//with the returned object, push it into current state, then turn off loading
movies.then(movieObj =>
this.setState({ movies: movieObj, loading: false })
);
};
render() {
const { movies } = this.state;
return (
<div className='App'>
<Header
submitHandler={this.submitHandler}
changeHandler={this.changeHandler}
/>
<HeroImage />
{this.state.loading ? <Loader /> : <MovieDisplay movies={movies} />}
</div>
);
}
MovieDisplay Component:
export default class MovieDisplay extends Component {
render() {
const { movies } = this.props;
return (
<div>
<MovieRow movies={movies.trending.results} movieType='trending' />
<MovieRow movies={movies.topRated.results} movieType='top rated' />
<MovieRow movies={movies.nowPlaying.results} movieType='now playing' />
<MovieRow movies={movies.upcoming.results} movieType='upcoming' />
</div>
);
}
}
MovieRow Component:
export default class MovieRow extends Component {
render() {
const { movieType, movies } = this.props;
return (
<div>
<div className='row-title'>{movieType}</div>
{console.log(movies)} //This part seems to somehow mount even when conditional rendering says it shouldn't!
<Slider {...settings} />
</div>
);
}
}
I then have the body do a conditional render like so, so that if the loading is complete (loading: false), then my MovieDisplay component should render, otherwise it's still loading, so show the Loader component.
I confimed that this part is working (if I search the React Devtools for Loader when loading: false it does not exist, but MovieDisplay does exist.
I'm passing down a data object via props from Home > MovieDisplay > MovieRow, and then looping through the array to display more components.
However, on initial load, it seems the MovieRow (last, nested child component) is somehow being mounted for a quick second, because in the console it's logging 4 undefined statements briefly, before resolving with the proper data.
Main question: If the Parent Component is not rendered to the DOM, then the child components inside of the Parent should also not be rendered, right?
Secondary question: Is it possible that all the components in my app are rendering briefly for a second on initial load, despite having a conditional in the render() function? That's the only thing I can think of that's causing this.
Example: If MovieDisplay is not rendered, then everything inside of it like MovieRow should also not be rendered, correct?
Hope this isn't too confusing...please let me know if I need to edit my problem or elaborate.
<MovieDisplay />'srenderfunction will get called regardless of conditional. If you don't want it's render function to get called, then wrap in a lambda and conditionally invoke it.