Stu1986C
Stu1986C

Reputation: 1510

Update Table Contents on API Response

I'm very new to React and although made some progress that I'm happy with, I've hit a snag that I'm stuck with and have been for over a day.

I'm creating a small app that uses React at the frontend and a .NET Core API server-side to supply the data.

So far, the following code works:

import React, { Component } from 'react';

export class LGSRValidation extends Component {
  displayName = LGSRValidation.name

  constructor(props) {
    super(props);
    this.state = { lgsr: [], loading: true };

    fetch('api/FormValidation/LGSR')
      .then(response => response.json())
      .then(data => {
        this.setState({
          lgsr: data,
          loading: false
        });
      });
  }

  static renderLGSRTable(lgsr) {
    return (
      <table className='table'>
        <thead>
          <tr>
            <th>Job Number</th>
            <th>Job Type</th>
            <th>UPRN</th>
            <th>Gas Register Number</th>
            <th>Service Date</th>
          </tr>
        </thead>
        <tbody>
          <tr key={lgsr.jobnumber}>
            <td>{lgsr.jobNumber}</td>
            <td>{lgsr.jobType}</td>
            <td>{lgsr.uprn}</td>
            <td>{lgsr.gasRegisterNumber}</td>
            <td>{lgsr.serviceDate}</td>
          </tr>
        </tbody>
      </table>
    );
  }

  render() {
    let contents = this.state.loading
      ? <p><em>Loading...</em></p>
      : LGSRValidation.renderLGSRTable(this.state.lgsr);

    return (
      <div>
        <div>
          <h1>{this.displayName}</h1>
          <p>This component demonstrates fetching data from the server.</p>
          {contents}
        </div>
      </div>
    );
  }
}

What it quite simply does is call my API to return a single object to the client-side code and render each property within the object into a separate table column.

What I'm trying to do is introduce a button that onClick will call the API again, pull a new object and change the values in the table for the new property values. I'm happy with my server-side code and the creation of the random objects but my client side code is just not working.

I'm introducing the button within the parent tags of the component like this:

render() {
  let contents = this.state.loading
    ? <p><em>Loading...</em></p>
    : LGSRValidation.renderLGSRTable(this.state.lgsr);

  return (
    <div>
      <div>
        <h1>{this.displayName}</h1>
        <p>This component demonstrates fetching data from the server.</p>
        {contents}
      </div>


      <button onClick={this.getData}>
        Next
      </button>
    </div>
  );
}

Which seems to render fine. I'm also introducing a getData function like this further up:

getData() {
    fetch('api/FormValidation/LGSR')
        .then(response => response.json())
        .then(data => {
            this.setState({
                lgsr: data,
                loading: false
            });
        });

    this.setState({
       contents : this.state.lgsr
    });
}

I suspect it's at this part where I'm going wrong.

Please can you advise how I can click my new button to call the API, return some data and pass the properties of the lgsr object into the corresponding table columns; replacing the previous data with the new data.

Upvotes: 3

Views: 1958

Answers (3)

Jaz
Jaz

Reputation: 135

What are you trying to achieve here? Can you paste your final code?

I see something terrible here. Your 'loading' is already declared in your component state and you're updating 'contents' state with the lgsr data. And in your render, you have declared contents as this.state.loading

 constructor(props) {
    super(props);
    this.state = { lgsr: [], loading: true };

     /*some code here*/

    getData () {
        fetch('api/FormValidation/LGSR')
            .then(response => response.json())
            .then(data => {
                this.setState({
                    lgsr: data,
                    loading: false 
               });
            });

        this.setState({
           contents : this.state.lgsr
        });
    }

      render() {
        let contents = this.state.loading

Upvotes: 0

sn42
sn42

Reputation: 2444

I see 4 ways to improve your code:

  1. Load data in the componentDidMount lifecycle method instead of the component's constructor (more info). This should not be related to you problem but it's safer to ensure the component did mount before you possibly set the state.

  2. As SGhaleb mentioned be sure that your getData function is bound, either using an arrow function or the bind alternative, otherwise this.getData is undefined and the button's onClick should be a no op. You could use getData for your initial request to not have duplicated code in your class.

  3. In getData you set the state's content property, but you do it outside of the request's callback. It's value may be either the new or the old lgsr object of the new request. Check that you do want this or move it to the fetch callback.

  4. Use a dedicated component to render the LGSR table instead of using a static render method and pass the required data via props to it. Again, nothing to do with your question.

Upvotes: 3

SGhaleb
SGhaleb

Reputation: 1007

You need to bind your getData() to your constructor so you can use this for your component rather than the current function.

this.getData = this.getData.bind(this);

A quicker way is to use arrow function like the following...

getData = () => {....}

This should hopefully fix your problem and allow you to use this.state

Upvotes: 3

Related Questions