That Guy
That Guy

Reputation: 154

Why isn't the child component updating?

When I change the name of a product, in this case of the first one, I can track the changes from App -> List. However list_item doesn't get its props updated. I thought the problem was that the component is not rerendering but it is. It's the props that are not being updated and I don't know why.

app.js

import React, { Component } from 'react';

import List from './list';

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

    this.state = {
      products: [
        {
          name: 'TV',
          price: 1000,
          currency: 'USD'
        },
        {
          name: 'SSD',
          price: 100,
          currency: 'USD'
        }
      ],

      name: '',
      price: '',
      currency: ''
    };
  }

  handleChange = event => {
    console.log(`${event.target.name}: ${event.target.value}`);
    this.setState({ [event.target.name]: event.target.value });
  };

  changeState = e => {
    e.preventDefault();
    let products = [...this.state.products];
    products[0].name = this.state.name;
    products[0].price = this.state.price;
    products[0].currency = this.state.currency;

    this.setState({
      products
    });
  };

  render() {
    return (
      <div>
        <button onClick={() => console.log(this.state)}>log</button>
        <List products={this.state.products} />

        <p>{this.state.products[0].name}</p>

        <form onSubmit={this.changeState}>
          Name:
          <br />
          <input
            type="text"
            name="name"
            // defaultValue={this.state.product.name}
            onChange={this.handleChange}
          />
          <br />

          // for simplicity I'm skipping price and currency 
          // but they are there

          <input type="submit" value="Update" />
        </form>
      </div>
    );
  }
}

export default App;

list.js

import React, { Component } from 'react';
import ListItem from './list_item';

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

    this.state = props;
  }

  render() {
    const productItems = this.state.products.map((product, i) => {
      console.log(product.name);
      return (
        <ListItem
          key={i}
          id={i}
          name={product.name}
          price={product.price}
          currency={product.currency}
        />
      );
    });
    return (
      <table>
        <tbody>
          <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Price</th>
            <th>Currency</th>
            <th>Permissions</th>
          </tr>
          {productItems}
        </tbody>
      </table>
    );
  }
}

export default List;

list_item.js

import React, { Component } from 'react';

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

    this.state = props;
  }

  render() {
    console.log(this.state);
    console.log('rendering');
    return (
      <tr>
        <td>{this.state.id}</td>
        <td>{this.state.name}</td>
        <td>{this.state.price}</td>
        <td>{this.state.currency}</td>
      </tr>
    );
  }
}

export default ListItem;

Upvotes: 2

Views: 41

Answers (1)

Kevin Amiranoff
Kevin Amiranoff

Reputation: 14468

The issue you have in list_item.js is this.state = props inside the constructor. The constructor is only called once, so the state is defined when you initialize the component but never updated.

You don't really need the state in this component:

<tr>
        <td>{this.props.id}</td>
        <td>{this.props.name}</td>
        <td>{this.props.price}</td>
        <td>{this.props.currency}</td>
      </tr>

And because you don't need the state you could use a stateless component:

const ListItem = ({id, name, price, currency}) => (
  <tr>
    <td>{id}</td>
    <td>{name}</td>
    <td>{price}</td>
    <td>{currency}</td>
  </tr>
);

And by the way, you also have the same issue in list.js, that could also be a stateless component.

If you need to update your local state from the props, you should have a look at getDerivedStateFromProps or componentDidUpdate.

But in your case, you don't need to.

Upvotes: 1

Related Questions