Alexander
Alexander

Reputation: 1378

cannot access the data inside an array

I'm having trouble accessing data inside my state which I fetch from my database, this is my state:

state = {
    inputDataArr: [],
    inputDataArrTest: [
      {
        formID: 'test',
        inputArr: [1, 2, 3],
        labelArr: [4, 5, 6]
      }
    ]
  };

this is the collection I'm importing from the database:

{
    "_id": {
        "$oid": "5ba96b8ebffd923734090df4"
    },
    "inputArr": [
        "value1",
        "value2",
        "value3"
    ],
    "labelArr": [
        "label1",
        "label2",
        "label3"
    ],
    "formID": "5ba96b83bffd923734090df0",
    "__v": 0
}

which is an object with 2 arrays in it, this is how I fetch it :

componentWillMount() {
    fetch('/api/datas')
      .then(data => data.json())
      .then(datas => {
        const filterdDatas = datas.filter(
          data => data.formID == this.props.match.params.id
        );
        this.setState(currentState => ({
          inputDataArr: [...currentState.inputDataArr, ...filterdDatas]
        }));
      });
  }

now when I console log inputDataArr and inputDataArrTest I get an array with an object inside, exactly the same, but when I console log InputDataArrTest[0] which is what you can see in my state I can see the arrays and the formID, but when I console log inputDataArr[0] I get undefined, really frustrating, anybody knows why?

Upvotes: 0

Views: 126

Answers (4)

Itamar
Itamar

Reputation: 1634

Mapping and doing data manipulation inside the render is never a good idea and isn't easy to debug.

My suggestion is to initialize the state to an empty array, and call a method to return the mapped data.

constructor(props) {
    super(props);
    this.state = {
        inputDataArr: []
    };
}

render() {
    const data = this.state.inputDataArr.map(...)
    return <div>{data}</div>
}

This way state is initiated before first render and data will return an empty array. After componentDidMount the state will update and will re-render with the new data.

As a side note, i would do the mapping in a third then and set the state to the result itself.

Happy Coding.

Upvotes: 1

Alexander
Alexander

Reputation: 1378

 renderLabels() {
    const { inputDataArr } = this.state;
    return (
      !!inputDataArr.length &&
      inputDataArr[0].labelArr.map((label, index) => (
        <th key={index}>{label}</th>
      ))
    );
  }

conditional rendering solved my problem

Upvotes: 0

Sagiv b.g
Sagiv b.g

Reputation: 31024

I'm not sure where are you running the console.log but setState has a callback method that you can run after the actual data has been updated:

this.setState(currentState => ({
  inputDataArr: [...currentState.inputDataArr, ...filterdDatas]
}), () => {
    // this is the callback for setState, you can access the updated data
  console.log(inputDataArr);
}); 

Note If you run the console.log in render keep in mind that the first render call will always run before the async data has been fetched.

Note #2 Do not use componentWillMount at all and especially not for fetching data.
Use componentDidMount instead.

Upvotes: 1

mcssym
mcssym

Reputation: 944

  1. Have you test filteredDatas before using ...filteredDatas ? Maybe it is not an Array and result to other thing or an empty Array.
  2. Are you sure this.props.match.params.id is defined and equal to the formID property?
  3. If it exists only one object with unique formID why don’t you use Array#find instead of Array.filter and then update your state with

    this.setState(prevState => ({ dataArr: filteredDatas ? prevState.dataArr.concat([filteredDatas]) : prevState.dataArr // Here with Array#find, filteredDatas is null or a object not an Array });

Upvotes: 0

Related Questions