1

/*After fetching data and setting to state, I am attempting to generate an array of jsx items to display. But the array is showing as empty and nothing is rendering.

Tried, hardcoding and this works. Tried loggin the data and it does show up that state is receving the data.

Removed my authorization token from the code below. */

import React, { Component } from 'react';
import AddCard from '../AddCard/AddCard.js';
import KanCard from '../KanCard/KanCard.js';
import './CardHolder.scss';

export default class CardHolder extends Component {
  constructor(props){
    super(props);
    this.state = {
      stories: [],
      inProgressTasks: [],
      completeTasks: [],
    };
  }

  componentDidMount(){
    // let id = this.props.id;
    let id = 168881069;
    let completetask = [];
    let progresstask =[];
    var data = null;
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = false;

    xhr.addEventListener("readystatechange", function () {
      if (this.readyState === 4) {
        let parsedResponse = JSON.parse(this.responseText);
        for( let taskResponse of parsedResponse ){
          let task = {
              key:taskResponse.id,
              id:taskResponse.id,
              story_id:taskResponse.story_id,
              complete:taskResponse.complete,
              description: taskResponse.description,
        }
          if(!taskResponse.complete){
            progresstask.push(task)
          } else {
            completetask.push(task);
          }
        }
      }
    });
    this.setState({inProgressTasks:progresstask, completeTasks:completetask})
    xhr.open("GET", `https://www.pivotaltracker.com/services/v5/projects/2401708/stories/${id}/tasks`);
    xhr.setRequestHeader("X-TrackerToken", "296912a3ff4ddcda26b4a419934b3051");
    xhr.setRequestHeader("Accept", "*/*");
    xhr.setRequestHeader("Cache-Control", "no-cache");
    xhr.setRequestHeader("cache-control", "no-cache");
    xhr.send(data);
  }

    render(){

      let completeTasks = this.state.completeTasks.map((task)=>{
        return (
          <KanCard
          key = {task.id}
          id = {task.id}
          story_id = {task.story_id}
          complete = {task.complete}
          description = {task.description}
          />
        )
      })
      let inProgressTasks = this.state.inProgressTasks.map((task)=>{
        return (
          <KanCard
            key = {task.id}
            id = {task.id}
            story_id = {task.story_id}
            complete = {task.complete}
            description = {task.description}
            />
        )
      })
      console.log(inProgressTasks)
      return (
          <div className='holder'>
            <h2> {this.props.title} </h2>
            <div>
              <h3>In Progress</h3>
              {inProgressTasks}
            </div>
            <div>
              <h3>Complete</h3>
              {completeTasks}
            </div>

            <AddCard />
          </div>
      )
    }  
}
graveltrunk
  • 75
  • 2
  • 6

1 Answers1

1

There are a few issues with the way you're setting your call up and updating your state.

First, make sure you update your state when you get your response back, after all, it's an asynchronous request and you need to wait to get something, then update your state.

xhr.addEventListener("readystatechange", function () {
      if (this.readyState === 4) {
        let parsedResponse = JSON.parse(this.responseText);
        for( let taskResponse of parsedResponse ){
          let task = {
              key:taskResponse.id,
              id:taskResponse.id,
              story_id:taskResponse.story_id,
              complete:taskResponse.complete,
              description: taskResponse.description,
        }
          if(!taskResponse.complete){
            progresstask.push(task)
          } else {
            completetask.push(task);
          }
        }

        this.setState({inProgressTasks:progresstask, completeTasks:completetask})
      }
    });

Second, remember you're inside a class, so this.readyState and this.responseText are referencing the class when you use the keyword this, not your XHR object as you're expecting it to. In order to make this work, you should change the readystatechange's callback function to a lambda function, then replace the this for xhr, yet you should keep the this that actually makes a reference to your class in the this.setState:

xhr.addEventListener("readystatechange", () => {
          if (xhr.readyState === 4) {
            let parsedResponse = JSON.parse(xhr.responseText);
            for( let taskResponse of parsedResponse ){
              let task = {
                  key:taskResponse.id,
                  id:taskResponse.id,
                  story_id:taskResponse.story_id,
                  complete:taskResponse.complete,
                  description: taskResponse.description,
            }
              if(!taskResponse.complete){
                progresstask.push(task)
              } else {
                completetask.push(task);
              }
            }

            this.setState({inProgressTasks:progresstask, completeTasks:completetask})
          }
        });

I tried to replicate your issue here:

Edit musing-burnell-idpl6

I'm hitting a dumb api and updating my state with the response data. Play around with it. Change the readystatechange's callback function from being a lambda function to being an anonymous function as you initially set up and see what happens.

To read more on the this problem, take a look at this question.

Elder
  • 1,475
  • 9
  • 17
  • When I move this.setState as above i am prometed that it is not a function. TypeError: this.setState is not a function – graveltrunk Oct 08 '19 at 19:03
  • Did you convert the callback function from `function () { ` to `() => {`? – Elder Oct 08 '19 at 20:10
  • I will try it out. – graveltrunk Oct 08 '19 at 20:51
  • 1
    That did it. Thanks so much. Is that becasue this was incorrectly bound when not using the arrow function? – graveltrunk Oct 08 '19 at 20:54
  • Glad to hear it worked! No, it's because anonymous function do not behave well inside a class and by not behaving well I mean that when you have an anonymous function, the word `this` inside that function makes a reference to the scope of that function and not to the scope of the class. When you change from being an anonymous function to being a lambda function, then the word `this` inside the function goes back to referencing the scope of the class. – Elder Oct 08 '19 at 20:56