DEV Community

Giuseppe
Giuseppe

Posted on

Stuck on the meaning of this.props.addDog(this.state.newDog)

I'm studying a React app made up by one main component: App.js (the main one) and three other external components: Dog.js, DogItem.js, AddDog.js The App contain a set of items (Dogs): Dog.js, made up of single dog elements, DogItem.js, and a form: AddDog.js to add a new item: dog. In the AddDog.js file, the only line I don't realize is this.props.addDog(this.state.newDog); I have highlighted it below.

I'd like to underline that addDog in this.props.addDog(this.state.newDog); is different from the name of the component AddDog.
Thank you.

Here's AddDog.js

import React, { Component } from 'react';

class AddDog extends Component {
  constructor() {
    super();
    this.state = {
      newDog:{}
    }
  }

  static defaultProps = {
    categories: ['Web Design', 'Web Development', 'Mobile Development']
  }

  handleSubmit(e) {
    if(this.refs.name.value === '') {
      alert('Title is required');
    } else if (this.refs.image.value === '') {
        alert('Image link is required');
    } else if (this.refs.breed.value === '') {
          alert('Breed is required');
    } else {
      this.setState({newDog:{
        name: this.refs.name.value,
        breed: this.refs.breed.value,
        image: this.refs.image.value
      }}, function() {
        this.props.addDog(this.state.newDog); // <<<<<<<<<<<<<<<<<
      });
    }
    e.preventDefault();
  }

  render() {
    return (
      <div>
        <h3 id="addDog">Add Dog</h3>
        <form onSubmit={this.handleSubmit.bind(this)}>
          <div>
            <label>Name</label><br />
            <input id="dogName" type="text" ref="name" />
          </div>
          <div>
            <label>Image</label><br />
            <input id="imageURL" type="text" ref="image" />
          </div>
          <div>
            <label>Breed</label><br />
            <input id="dogBreed" type="text" ref="breed" />
          </div>
          <br />
          <input id="submitButton" type="submit" value="Submit" />
          <br />
        </form>
      </div>
    );
  }
}

export default AddDog;

Here's the App.js

import React, { Component } from 'react';
import Dogs from './components/Dogs';
import DogItem from './components/DogItem';
import AddDog from './components/AddDog';
import './App.css';

class App extends Component {
  constructor() {
    super();
    this.state = {
      dogs: []
    };
  }

  getDogs() {
    var defaultDogs = {dogs: [
      {
        name: 'Princess',
        breed: 'Corgi',
        image: 'https://s-media-cache-ak0.pinimg.com/originals/51/ae/30/51ae30b78696b33a64661fa3ac205b3b.jpg'
      },
      {
        name: 'Riley',
        breed: 'Husky',
        image: 'http://portland.ohsohandy.com/images/uploads/93796/m/nice-and-sweet-siberian-husky-puppies-for-free-adoption.jpg'
      },
    ]}; 
    this.setState(defaultDogs);
  }

  componentWillMount() { // this soon display the two dogs before the render
    this.getDogs();  
  }

  handleAddDog(dog) {
    let dogs = this.state.dogs;
    dogs.push(dog);
    this.setState({dogs:dogs});
  }

  handleDeleteDog(name) {
    let dogs = this.state.dogs;
    let index = dogs.findIndex(x => x.name === name); // function (x) {return x.name === name} is like  x => x.name === name
    dogs.splice(index, 1);
    this.setState({dogs:dogs});
  }

  render() {
    return (
      <div className="App">
        <Dogs dogs={this.state.dogs} onDelete={this.handleDeleteDog.bind(this)} />
        <AddDog addDog={this.handleAddDog.bind(this)} />
        <hr />
      </div>
    );
  }
}

export default App;

Here's Dog.js

<pre>
import React, { Component } from 'react';
import DogItem from './DogItem';

class Dogs extends Component {
    deleteDog(name) {
        this.props.onDelete(name);
    }

    render() {
        let dogItem;
        if (this.props.dogs) {
            dogItem = this.props.dogs.map(dog => {
                return (
                    <DogItem onDelete={this.deleteDog.bind(this)} key={dog.name} dog={dog} />
                );
            });
        }
        return (
            <div className="Dogs">
                <h1>Good Dogs</h1>
                {dogItem}
            </div>
        );
    }
}

export default Dogs;

Here's DogItem.js

import React, { Component } from 'react';

class DogItem extends Component {
  deleteDog(name) {
    this.props.onDelete(name);
  }

  render() {
    return (
      <ul className="Dog">
        <img src={this.props.dog.image} href={this.props.dog.image} role="presentation"  width="100" height="100"></img>
        <br></br>
        <strong>{this.props.dog.name}</strong>: {this.props.dog.breed} <a href="#" onClick={this.deleteDog.bind(this, this.props.dog.name)}>X</a>
        <br></br>
      </ul>
    );
  }
}

export default DogItem;

Discussion (2)

Collapse
giuseppe profile image
Giuseppe Author • Edited on

Ok, I've just found the meaning.
It creates a new dog item, sets the state of the child and passes to the parent component the state of the child to the prop addDog that will fire the bound function handleAddDog in App.js.

Collapse
dance2die profile image
Sung M. Kim

Yes. I think you got it.

React has one way data flow.
So when you want to pass child component's data (a new dog) back to the parent (which is the one that maintains the list of dogs), you pass a callback to the child component, which will call the handler with the newly created dog object.

Simply put, this.props.addDog passes form data back to App component.

I've spotted a bug in App.handleAddDog, in which you are mutating the this.state.dogs by pushing the dog to the state directly.

You should not mutate the state object directly as React would not know if the this.state.dogs has not changed.

So instead of

 handleAddDog(dog) {
    let dogs = this.state.dogs;
    dogs.push(dog);
    this.setState({dogs:dogs});
  }

create a copy (a new reference of dogs) and set it as a new state.

 handleAddDog(dog) {
    // This will create a copy of `this.state.dogs`
    const { dogs } = this.state;
    // or you can do
    const dogs = {...this.state.dogs};
    // then push the `dog` to the new dogs state object
    dogs.push(dog);

    // Now `dogs` has a new reference and React will re-render `Dogs` component
    this.setState({dogs:dogs});
  }

The reason for passing a new reference of dogs is an advanced concept on how React reconciles changes.

Simply put, it's for React not having to do deep property change checks

If you want to know more on how React reconcilation diff'ing algorithm works, check out this post.

css-tricks.com/how-react-reconcili...