5

I faced a weird problem, I found the answer, however I want to know the reason behind the scene.

I have a react component which sends request toward server and after getting the result back, set it to the states of the component.

class Featured extends React.Component{
constructor(props){
    super(props);
    this.state = {
        data : ['', ''],
        error: null,
    }
}
componentDidMount(){
    axios.get('/api/featured')
        .then(res=>{ 
            let dataClone = [2];
            console.log('before');
            res.data.map((dt,i)=>{
                dataClone[i] = dt;
            });
            this.setState({
                data : dataClone
            })
            })
        .catch(err=>{this.setState({error: err}); errorHandling(err)});
}
render(){
    if(!this.state.error){
        return(
            <div>data: {this.state.data[1].title}</div>
        )
    }
    else {
        return (<div className='text-center text-warning'>There is some error loading the featured</div>)
    }
}
}

here's the data which sent back :

        {
            title : 'how to be a good programmer?',
            subtitle: 'I was wondering how can i be a good programer after 10 years experience',
            author : 'hamid mortazavi',
            date : new Date().toLocaleDateString(),
        },
        {
            title : 'Why are you alone?',
            subtitle: 'this is going to be a long story from here, but bear with me...',
            author : 'shahin ghasemi',
            date : new Date().toLocaleDateString(),
        }

This works correctly, however if I change the way I've done initialization for data state, it does not work. for example this three ways does not work:

this.state = {
//1:
data : new Array(2),
//2:
data : [],
//3:
data : [2]
}

none of these works, and when I want to show this.state.data[1].title for example, it does not work, and throw me Uncaught TypeError: Cannot read property 'title' of undefined. What surprises me is there is no problem displaying this.state.data[0].title and it seems the problem is the index.

As I said now I found the solution but it takes me 1 hour to solve that. I just want to know the reason and why ?

Shahin Ghasemi
  • 1,600
  • 1
  • 20
  • 38
  • 1
    None of the alternative initilizations actually contain a value for index `1`. You can convince yourself of this by typing `new Array(2)[1]`, `[2][1]`, `[][1]` into the console. They all return `undefined`. The rest is probably due to timing: before axios returns with data, you will have the initial data array whose value at index `1` is undefined and thus your render function will fail. if you initialize with `['', '']` instead, the render function will not fail because `this.state.data[1] === ''` and thus `this.state.data[1].title === undefined` (no error, but you won't see anything either). – FK82 Aug 19 '18 at 08:53
  • 1
    Note that javascript arrays don't have fixed length, see: https://stackoverflow.com/questions/20321047/how-are-javascript-arrays-represented-in-physical-memory – nijm Aug 19 '18 at 08:58
  • thanks for your feedback, so what should I do the in case of unknown length of data which is sent back from the server? say one time I have 3 items and another 10 items. in this case how should I initialize the state? @FK82 – Shahin Ghasemi Aug 19 '18 at 09:03
  • 1
    Just go with an empty array: `this.state = { data: [] };` and then check in your render method if there are items in the array before accessing any data: `if (this.state.data.length > 0) { /* access data properties */ }` – nijm Aug 19 '18 at 09:13
  • As @nijm said, initialize to empty (`[]` or even `null`) and then use [conditional rendering](https://reactjs.org/docs/conditional-rendering.html): `return (
    data: { this.state.data !== null ? this.state.data : 'none' }
    );` If you need a complete example, let me know.
    – FK82 Aug 19 '18 at 14:16
  • Yeah, I got it thank you alot @FK82 – Shahin Ghasemi Aug 19 '18 at 14:23
  • @ShahinSoft Sure! – FK82 Aug 20 '18 at 15:48

2 Answers2

1

This is not a problem of React but a problem of array items being undefined.

In your first example where you don't see the error, you actually defined the value of the 2 items in the state array by doing this

data : ['', ''],

This way, the value of data[0] and data[1] always exist and unless set by data response from server, will always have at least a value of an empty string.

In your second example, your data object is just a blank array of either no items ([]), 1 item ([2]), or 2 items (new Array(2)). Even with new Array(2), your data object items doesn't have a value defined. Try running this in your console:

x = new Array(2)
x[0] // will return undefined
x[1] // will return undefined
Arielle Nguyen
  • 2,932
  • 1
  • 20
  • 15
  • in the case of uncertainty, how should I initialize the state? please read my CM in @FK82 response – Shahin Ghasemi Aug 19 '18 at 09:06
  • I believe the best practice is to create an initiateState with default values. You should also use some type checking here. Then use Object.assign to create the state. Or you could double check if the value exist before calling to get any property on it. – Arielle Nguyen Aug 19 '18 at 09:08
  • it would be great, if you could edit and implement that in your code – Shahin Ghasemi Aug 19 '18 at 09:18
0

In the case that is not working you are setting the initial state.data to an empty array ( or in the last case to an array with one number 2 in it). When you call this.state.data[1].title you are trying to access a property on undefined, which give` you a null point error.

In the case that is working for you, you are trying access the property title on an empty string, which will be undefined, but it doesn't error because the thing you're trying to find the title of (the empty string) is not undefined.

Sam Rae
  • 61
  • 10