0

I'm trying to set a state from a fetch function and the api seems to return the json correctly. But the state never seems to be populated with the data from the api. Does anyone see the problem? The code seems to run fine and doesn't display any errors so I'm not sure what the problem is. Here is code:

var count = 0;
const ActivityRow = props => (
  <tr>
    <td>{props.activity.Date.toString()}</td>
    <td>{props.activity.ActivityType}</td>
    <td>{props.activity.Value}</td>
  </tr>
);

function ActivityTable(props) {
  const ActivityRows = props.activities.map(activity => (
    <ActivityRow key={activity.id} activity={activity} />
  ));
  return (
    <table className="bordered-table">
      <thead>
        <tr>
          <th>Date</th>
          <th>Activity Type</th>
          <th>Value</th>
        </tr>
      </thead>
      <tbody>{ActivityRows}</tbody>
    </table>
  );
}

function formatDate(date) {
  var hours = date.getHours();
  var minutes = date.getMinutes();
  var ampm = hours >= 12 ? "pm" : "am";
  hours = hours % 12;
  hours = hours ? hours : 12;
  minutes = minutes < 10 ? "0" + minutes : minutes;
  var strTime = hours + ":" + minutes + " " + ampm;
  return (
    date.getMonth() +
    1 +
    "/" +
    date.getDate() +
    "/" +
    date.getFullYear() +
    "  " +
    strTime
  );
}

class ActivityAdd extends React.Component {
  constructor() {
    super();
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleSubmit(e) {
    e.preventDefault();
    var form = document.forms.ActivityAdd;
    this.props.createActivity({
      id: count++,
      Date: formatDate(new Date()),
      ActivityType: form.Activity.value,
      Value: form.Value.value
    });
    // clear the form for the next input
    form.Activity.value = "";
    form.Value.value = "";
  }
  render() {
    return (
      <div>
        <form name="ActivityAdd" onSubmit={this.handleSubmit}>
          <input type="text" name="Activity" placeholder="Activity" />
          <input type="text" name="Value" placeholder="Value" />
          <button>Add</button>
        </form>
      </div>
    );
  }
}
class App extends React.Component {
  constructor() {
    super();
    this.state = { activities: [] };
    this.createActivity = this.createActivity.bind(this);
  }

  loadData() {
    fetch("/api/activities")
      .then(response => response.json())
      .then(data => {
        console.log("Total count of records:", data._metadata.total_count);
      });
    this.setState({ activities: data.records });
  }
  createActivity(newActivity) {
    fetch("/api/activities", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(newActivity)
    })
      .then(response => response.json())
      .then(updatedActivity => {
        const newActivities = this.state.activities.concat(newActivity);
        this.setState({ activities: newActivities });
      });
  }
  render() {
    return (
      <div>
        <h1>Diabetes Self Management App</h1>
        <hr />
        <ActivityTable activities={this.state.activities} />
        <hr />
        <ActivityAdd createActivity={this.createActivity} />
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById("contents"));

Any suggestions will be appreciated.

2
  • Where are you calling the loadData method? Commented Aug 13, 2018 at 2:50
  • this.setState({ activities: data.records }); move this line to then, although if u move it there, you should put this in another object before entering then function. Commented Aug 13, 2018 at 3:22

1 Answer 1

2

The setState is outside the scope of data, so data is undefined, which lead to the failure of setting state.

loadData() {
  fetch('/api/activities').then(response =>
    response.json()
  ).then(data => {
    console.log("Total count of records:", data._metadata.total_count);
  });
  // undefined here as data is not existed
  this.setState({ activities: data.records });
}

The solution is to move setState inside the promise return.

var count = 0;
const ActivityRow = props => (
  <tr>
    <td>{props.activity.Date.toString()}</td>
    <td>{props.activity.ActivityType}</td>
    <td>{props.activity.Value}</td>
  </tr>
);

function ActivityTable(props) {
  const ActivityRows = props.activities.map(activity => (
    <ActivityRow key={activity.id} activity={activity} />
  ));
  return (
    <table className="bordered-table">
      <thead>
        <tr>
          <th>Date</th>
          <th>Activity Type</th>
          <th>Value</th>
        </tr>
      </thead>
      <tbody>{ActivityRows}</tbody>
    </table>
  );
}
function formatDate(date) {
  var hours = date.getHours();
  var minutes = date.getMinutes();
  var ampm = hours >= 12 ? "pm" : "am";
  hours = hours % 12;
  hours = hours ? hours : 12;
  minutes = minutes < 10 ? "0" + minutes : minutes;
  var strTime = hours + ":" + minutes + " " + ampm;
  return (
    date.getMonth() +
    1 +
    "/" +
    date.getDate() +
    "/" +
    date.getFullYear() +
    "  " +
    strTime
  );
}

class ActivityAdd extends React.Component {
  constructor() {
    super();
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleSubmit(e) {
    e.preventDefault();
    var form = document.forms.ActivityAdd;
    this.props.createActivity({
      id: count++,
      Date: formatDate(new Date()),
      ActivityType: form.Activity.value,
      Value: form.Value.value
    });
    // clear the form for the next input
    form.Activity.value = "";
    form.Value.value = "";
  }
  render() {
    return (
      <div>
        <form name="ActivityAdd" onSubmit={this.handleSubmit}>
          <input type="text" name="Activity" placeholder="Activity" />
          <input type="text" name="Value" placeholder="Value" />
          <button>Add</button>
        </form>
      </div>
    );
  }
}
class App extends React.Component {
  constructor() {
    super();
    this.state = { activities: [] };
    this.createActivity = this.createActivity.bind(this);
  }

  loadData() {
    fetch("/api/activities")
      .then(response => response.json())
      .then(data => {
        console.log("Total count of records:", data._metadata.total_count);
        // must be in this then scope
        this.setState({ activities: data.records });
      });
    
  }
  createActivity(newActivity) {
    fetch("/api/activities", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(newActivity)
    })
      .then(response => response.json())
      .then(updatedActivity => {
        const newActivities = this.state.activities.concat(newActivity);
        this.setState({ activities: newActivities });
      });
  }
  render() {
    return (
      <div>
        <h1>Diabetes Self Management App</h1>
        <hr />
        <ActivityTable activities={this.state.activities} />
        <hr />
        <ActivityAdd createActivity={this.createActivity} />
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById("contents"));

Sign up to request clarification or add additional context in comments.

2 Comments

I tried this and it still didn't work. Do you have any other suggestions?
Have you tried console.log(data.records) inside the then scope to see what is being printed out?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.