Merianos Nikos Merianos Nikos - 21 days ago 6
Javascript Question

React seems like doesn't pass props to another component

I follow a tutorial on how to build a

React
application, and I am stacked with a kind of strange issue.

When I try to pass some information to another component, the other component getting the
props
but it is empty.

In my case, the
index.js
is like that:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import YTSearch from 'youtube-api-search';

import SearchBar from './components/search_bar';
import VideoList from './components/video_list';

const API_KEY = 'AIzaSyDdvc_zComCpdqqfmwgOsZvLOwwPEabcde';

class App extends Component {
constructor ( props ) {
super ( props );

this.state = {
videos : []
};
}

componentDidMount() {
YTSearch (
{
key : API_KEY,
term : 'surfboards'
},
videos => {
this.setState ( { videos } );
}
);
}

render () {
return (
<div>
<SearchBar />
<VideoList videos={this.state.videos} />
</div>
);
}
};

ReactDOM.render (
<App />,
document.querySelector ( '.container' )
);


and my code in the
./components/video_list.js
is like this:

import React, { Component } from 'react';

import VideoListItem from './video_list_item';

class VideoList extends Component {
constructor( props ) {
super( props );

this.state = {
videoItems : []
};
}

componentDidMount() {
this.setState(
{
videoItems: this.props.videos.map(
video => {
console.log( video );
return <VideoListItem video={video} />;
}
)
}
)
}

render() {
return (
<ul className="col-md-4 list-group">{ this.state.videoItems }</ul>
);
}
}

export default VideoList;


The code seems to be very strateforward, but in reality it has issues.

If I try this statement
console.log( this.state.videos );
before the
return
statement in the
index.js
I get the following output:

// Initially I get this because the API is not yet completed:
Array[0]
length: 0
__proto__: Array[0]

// And then, once the API Request it is completed I get this:
Array[5]
0: Object
etag : ""5C5HHOaBSHC5ZXfkrT4ZlRCi01A/2H00YaVLWV4Xof09xk9Q8k6vlxw""
id: Object
kind: "youtube#searchResult"
snippet: Object
__proto__: Object
1: Object
2: Object
3: Object
4: Object
length: 5
__proto__: Array[0]


At the same time if I try a
console.log( props )
inside the
constructor
method of the
VideoList
component I get the following output:

Object {videos: Array[0]}
videos: Array[0]
length: 0
__proto__: Array[0]
__proto__: Object


Do you have any idea of what can be wrong ? Do you see something I don't see ?

Answer

Regarding this -

At the same time if I try a console.log( props ) inside the constructor method of the VideoList component I get the following output:

Object {videos: Array[0]}
    videos: Array[0]
        length: 0
        __proto__: Array[0]
    __proto__: Object

This is absolutely correct behaviour.

This is happening because during react component life cycle your child component gets rendered first and at that time props which you are passing to your child component will be having default or empty values ( e.g []).

Now your child gets rendered the parent rendering happens.

When parent gets rendered completely componentDidMount method of parent gets called in which you have made some ajax request to download dynamic data which in your case is video lists.

Which you are doing like this and this is also perfectly valid -

componentDidMount() {
    YTSearch (
        {
            key  : API_KEY,
            term : 'surfboards'
        },
        videos => {
            this.setState ( { videos } );
        }
    );
}

After the data comes in via ajax you set the state again in your parent component which causes rendering cycle to happen again.

            this.setState ( { videos } );

Now your child will receive updated new video list array.

And rendering of child happens again which will be having video lists.

But since you have parent props changed and to receive new props you need to add a new life cycle method.

componentWillReceiveProps

Here you can compare old props with new props and set the states to the updated props. Which will render the child component with latest updated data.

componentWillReceiveProps(nextProps) {
        this.setState(
            {
                videoItems: nextProps.videos.map(
                    video => {
                        console.log( video );
                        return <VideoListItem video={video} />;
                    }
                )
            }
        )
}