Skip to content
Advertisement

React – Ajax data not being passed into Child Component

I have two components, one parent one child. I am using the fetch method in componentDidMount() callback. Once I do this, I set the state with key items to that data that is pulled from the api. Once I do this it should be able to be console logged in the child component as a prop. However this is not working. What am I doing wrong here?

Parent Component:

import React, {Component} from 'react';
import Map from './maps/Map';

class Main extends Component {
    constructor(props){
        super(props);
        this.state = {
            name: "John",
            items: []
        }
    }

    componentDidMount() {
        fetch('https://hn.algolia.com/api/v1/search?query=')
        .then(dat => dat.json())
        .then(dat => {
            this.setState({
                items: dat.hits
            })
        })
    }

    render() {
        return (
            <div>
                <Map list={this.state.name} items={this.state.items}></Map>
            </div>
        )
    }
}

export default Main;

Child Component:

import React, {Component} from 'react';

class Map extends Component {

    constructor(props) {
        super(props);
        console.log(props.items)
    }

    render () {
        return (
            <h1>{this.props.name}</h1>
        )
    }
}

export default Map;

Advertisement

Answer

The constructor for a component only runs one time during a lifecycle. When it does, props.items is undefined because your ajax request is in-flight, so console.log(props.items) doesn’t show anything.

If you change your constructor to console.log("constructed");, you’ll see one-time output (stack snippets may not show this–look in your browser console). Henceforth, componentDidUpdate() can be used to see the new props that were set when your ajax request finishes.

You could also log the props inside the render method, which will run once before the ajax request resolves and again afterwards when props.items changes.

As a side point, you have <Map list=... but the component tries to render this.props.name, which is undefined.

Also, if you aren’t doing anything in the constructor (initializing state or binding functions) as here, you don’t need it.

class Map_ /* _ added to avoid name clash */ extends React.Component {
  constructor(props) {
    super(props);
    console.log("constructed");
  }
  
  componentDidUpdate(prevProps, prevState) {
    const props = JSON.stringify(this.props, null, 2);
    console.log("I got new props", props);
  }

  render() {
    return ( 
      <div>
        <h1>{this.props.name}</h1>
        <pre>
          <ul>
            {this.props.items.map((e, i) => 
              <li key={i}>{JSON.stringify(e, null, 2)}</li>)}
          </ul>
        </pre>
      </div>
    );
  }
}

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {name: "John", items: []};
  }

  componentDidMount() {
    fetch('https://hn.algolia.com/api/v1/search?query=')
      .then(dat => dat.json())
      .then(dat => {
        this.setState({items: dat.hits})
      });
  }

  render() {
    return (
      <div>
        <Map_
          name={this.state.name} 
          items={this.state.items}
        />
      </div>
    );
  }
}

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<Main />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement