Table doesn't update after create, edit or delete new row

Viewed 334

I'm using ReactJs with Material-UI to display a table of Car components but never update after Creating, Editing or Deleting a row. Next is the structure:

class MainCar extends React.Component {
  constructor(props) {
        super(props);
        this.state = {
          cars: []
        };
    this.apiUrl = "http://localhost:8080/api/v1/cars";
    this.onCreate = this.onCreate.bind(this);
    this.onUpdate = this.onUpdate.bind(this);
    this.onDelete = this.onDelete.bind(this);
    this.loadFromServer = this.loadFromServer.bind(this);
  }
  loadFromServer() {
    fetch(this.apiUrl)
        .then(response => response.json())
        .then(json => {
           this.setState({
             cars: json.cars
           });
        });
  }
  onCreate(newCar) {
    try {
      const result = 
      fetch(this.apiUrl, {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(newCar)
      });
    } catch(e) {
      console.error(e);
    }
    this.loadFromServer();
  }
  onUpdate(car, updatedCar) {
    try {
      const result = 
        fetch(car._links.self.href, {
          method: 'PUT',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(updatedCar)
        });
    } catch(e) {
      console.error(e);
    }
    this.loadFromServer();
  }
  onDelete(car) {
    try {
      const result = 
        fetch(car._links.self.href, {
          method: 'DELETE',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(car)
        });
    } catch(e) {
      console.error(e);
    }
    this.loadFromServer();
  }
  render() {
    return (
      <CarsTable cars={this.state.cars}
        onCreate={this.onCreate}
        onUpdate={this.onUpdate}
        onDelete={this.onDelete} />
    );
  }
}

class CarsTable extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    const cars = this.props.cars.map(car =>
      <Car key={car._links.self.href}
        car={car}
        onUpdate={this.props.onUpdate}
        onDelete={this.props.onDelete} />
    );
    return (
      <Table>
        <TableBody>
          {cars}
        </TableBody>
      </Table>
    );
  }
}

class Car extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <TableRow>
          <TableCell>{car.code}</TableCell>
          <TableCell>{car.color}</TableCell>
      </TableRow>
    );
  }
}

As you seen, MainCar has cars array on its state, but CarTable and Car have only properties.

When I log on render functions I see data has changed, however the view is not updated.

View only updates

  • When I press F5 to update page.
  • Or when I create, update or delete a new Row I see the previous change but not the last one.

I read that React re-render a component when state has changed. Should I set state for CarTable and Car components copying from props? How can I solve this problem?

Thanks in advance.

3 Answers

What is probably happening is that your GET request is returning before your POST/PUT/DELETE is complete. One way to resolve this is to make sure the GET is only fired after the other action has completed by putting it in a then block.

e.g.

onDelete(car) {
    try {
      const result = 
        fetch(car._links.self.href, {
          method: 'DELETE',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(car)
        }).then(a => this.loadFromServer());
    } catch(e) {
      console.error(e);
    }
  }

Your code looks like it should work (I know that's not the most helpful thing to hear when it clearly deosn't), but here's a few things you could try. Try doing bind(this) on your functions in the constructor, and also try calling this.loadFromServer(), not loadFromServer(). Also, I'm guessing you just left this out of your post but you're missing onDelete

See below (look at constructor and onCreate method):

class MainCar extends React.Component {
  constructor(props) {
        super(props);
        this.loadFromServer = this.loadFromServer.bind(this);
        this.onCreate = this.onCreate.bind(this);
        this.onUpdate = this.onUpdate.bind(this);
        this.state = {
          cars: []
        };
  }
  loadFromServer() {
    fetch(this.apiUrl)
        .then(response => response.json())
        .then(json => {
           this.setState({
             cars: json.cars
           });
        });
  }
  onCreate() {
    // more code to create...
    this.loadFromServer(); // to update cars state
  }
  onUpdate() {
    // more code to put...
    this.loadFromServer(); // to update cars state
  }
  render() {
    return (
      <CarsTable cars={this.state.cars}
        onCreate={this.onCreate}
        onUpdate={this.onUpdate}
        onDelete={this.onDelete} />
    );
  }
}

You need to await asynchronous things in JavaScript. Otherwise, you're going to be doing your POST and GET in parallel instead of sequentially. You can use an async function to make this easier.

async onCreate(newCar) {
  try {
    await fetch(this.apiUrl, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(newCar)
    });
  } catch(e) {
    console.error(e);
  }

  return this.loadFromServer();
}
Related