0

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?

1
  • Where are you doing the console.log? In the render method? There will always be a first render without the async data Commented Sep 25, 2018 at 17:30

4 Answers 4

1

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.

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

Comments

1

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.

3 Comments

thank you for your comment, I copied your code and changed to DidMount and still I get inputDataArr is undefined
inputDataArr is undefined or inputDataArr[0] is undefined?
yes I posted a solution that worked for me thanks to another stack overflow post, thank you for your help!
0
  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 });

Comments

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

conditional rendering solved my problem

2 Comments

also, debugging it will be hell
.length will never be -n so its either 0 or above. hence there's no reason to coerces to a Boolean. inputDataArr.length && ... should be enough. By the way, your question lacks a lot of important details (such as your render method)

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.