Reputation: 9649
I have the following code which is working as expected:
import React, { Component } from 'react';
let result = null;
class MyData extends Component {
_getData = () => {
fetch(url)
.then(response => response.json())
.then(data => {
result = items.find(
item => item.id === 'abcd'
);
});
};
componentDidMount() {
this._getData();
}
render() {
return result ? <span>hello</span> : null;
}
}
When I try to move result
as an instance variable of the class as follows, it always remains null in the render()
method. Though the assignment is working correctly post fetch:
import React, { Component } from 'react';
class MyData extends Component {
result = null;
_getData = () => {
fetch(url)
.then(response => response.json())
.then(data => {
this.result = items.find(
item => item.id === 'abcd'
);
});
};
componentDidMount() {
this._getData();
}
render() {
return this.result ? <span>hello</span> : null;
}
}
Can someone explain me what is the expected usage here. And how things have changed from ES6 onwards.
Upvotes: 1
Views: 195
Reputation: 3662
The reason you are seeing null is because the state is not being updated in the code example you posted. The component mounts and this.result
is null. But when the _getData
method is called and the response comes back, the component is not being rendered.
You should be storing this data in your state, as React relies on the state changes to know when it should re-render the component. Try something like this:
import React, { Component } from 'react';
class MyData extends Component {
state = {
result: null
}
_getData = () => {
fetch(url)
.then(response => response.json())
.then(data => {
this.setState({
result: items.find(
item => item.id === 'abcd'
)
})
});
};
componentDidMount() {
this._getData();
}
render() {
const { result } = this.state
return result ? <span>hello</span> : null;
}
}
You can learn more about rendering, state, and component lifecycles here: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
Edit: If you want to experiment with the new react hooks api, you could convert your component to look like the folllowing:
import React, { useEffect, useState } from 'react';
const MyData = ({ ...props }) => {
const [result, setResult] = useState(null);
const getData = () => {
fetch(url)
.then(response => response.json())
.then(data => {
const result = items.find(item => item.id === 'abcd');
setResult(result);
});
};
useEffect(() => {
getData();
}, []);
return result ? <span>hello</span> : null;
}
Upvotes: 1
Reputation: 340
I think the main thing you're missing is state. Keep in mind that these Components you are extending are React Component classes which have a very specific life cycle. Look into component life cycle for more info. This will work as expected with the following refactor:
import React, { Component } from 'react';
class MyData extends Component {
constructor() {
super();
this.state = {result: null};
}
_getData = () => {
fetch(url)
.then(response => response.json())
.then(data => {
let resp = data.items.find(
item => item.id === 'abcd'
);
this.setState({ result: resp });
});
};
componentDidMount() {
this._getData();
}
render() {
return this.state.result ? <span>hello</span> : null;
}
}
Upvotes: 0
Reputation: 53874
Both examples just don't work, the reason is that componentDidMount
lifecycle fires after render
lifecycle.
Because you don't cause re-render (with setState
) in any phase, nothing will cause an additional render to read the updated value of result
.
Therefore, the next example will always render No Result
import ReactDOM from 'react-dom';
import React, { Component } from 'react';
let result = null;
class App extends Component {
thisResult = null;
setResult = () => {
result = 10;
this.thisResult = 10;
};
componentDidMount() {
this.setResult();
}
render = () => {
return <div>{result || this.thisResult ? 'Result' : 'No Result'}</div>;
};
}
ReactDOM.render(<App />, document.getElementById('root'));
Upvotes: 4