简体   繁体   中英

Passing state into child component as props not working

I'm trying to build an example CRUD app with React and React Router, and I can't figure out why state isn't passing into a child component the way I'm expecting it to. When I hit the edit route, it renders the Edit component, which grabs the kitten I want from the database and sends it's info to a Form component which is used both for editing an existing kitten or adding a new one.

Here's the Edit component:

import React, { Component } from 'react';
import axios from 'axios';
import { match } from 'react-router-dom';
import Form from './Form';


export default class Edit extends Component {
    constructor(props) {
        super(props)

        this.state = {}
    }

    componentDidMount() {
        axios.get(`/updateKitten/${this.props.match.params.id}`)
        .then(res => {
            const kitten = res.data
            this.setState({ kitten })
            console.log(this.state.kitten.name)  //Sammy, or something
        })
        .catch(err => console.log(err))
    }

    render() {
        return (
                <Form 
                    name={this.state.kitten.name} // throws error or undefined
                    description={this.state.kitten.description} //throws error or undefined
                    route={this.props.match.params.id} 
                />
        )
    }
}

The Edit component passes name, description, and route to this Form component:

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

export default class Add extends Component {
  constructor(props) {
    super(props)

    this.state = { name: this.props.name, description: this.props.description}

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e) {
    const name = e.target.name;
    const value = e.target.value;

    this.setState({
      [name]: value
    });
  }

  handleSubmit(e) {
    axios.post(`/addKitten/${this.props.route}`, this.state)
      .then(this.setState({ name: '', description: '' }))
    e.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>Name</label>
        <input type='text' name="name" value={this.state.name}
         onChange={this.handleChange}/>
        <label>Description</label>
        <input type='text' name="description" value={this.state.description}
          onChange={this.handleChange}/>
        <input type='submit' value='Submit' />
      </form>
    )
  }
}

And I get the following error:

bundle.js:28950 Uncaught TypeError: Cannot read property 'name' of undefined

from trying to send that info as props to the Form component.

What am I doing wrong?

When your Edit component loads for the first time, it doesn't have a kitten . Hence the error.

What you need to do is the create an empty kitten. In your Edit component...

this.state = { kitten: {name:''} }

This will set the kitten's name to an empty string on the very first mount/render of your component.

Two things,

First: In your edit component, you have not initialised kitten state and you are setting it based on the API result. However, componentDidMount is called after the component has been called and hence the DOM has been rendered once and the first time it did not find any value as this.state.kitten.name or this.state.kitten.description .

Its only after the API is a success that you set the kitten state. Hence just make a check while rendering.

Second: You have console.log(this.state.kitten.name) after the setState function. However setState is asynchronous . See this question:

Change state on click react js

and hence you need to specify console.log in setState callback

You code will look like

export default class Edit extends Component {
    constructor(props) {
        super(props)

        this.state = {}
    }

    componentDidMount() {
        axios.get(`/updateKitten/${this.props.match.params.id}`)
        .then(res => {
            const kitten = res.data
            this.setState({ kitten }. function() {
                  console.log(this.state.kitten.name)  //Sammy, or something
            })

        })
        .catch(err => console.log(err))
    }

    render() {
        return (
                <Form 
                    name={this.state.kitten.name || '' } 
                    description={this.state.kitten.description || ''} 
                    route={this.props.match.params.id} 
                />
        )
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM