Reputation: 13
I'm trying to set a variable with useState
after an API call, but it doesn't work. Debugging by reactotron, he makes the API call, but he doesn't set the variable.
export default function Forecast({ navigation }) {
const [cityData, setCityData] = useState([]);
const idNavigation = navigation.state.params.cityData.woeid;
async function loadCityData(cityID) {
const response = await api.get(`${cityID}`);
setCityData([response]);
console.tron.log(cityData);
}
useEffect(() => {
if (idNavigation) {
loadCityData(idNavigation);
}
return () => {};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [idNavigation]);
return <Text>Forecast Weather</Text>;
}
Forecast.propTypes = {
navigation: PropTypes.shape({
state: PropTypes.object,
}).isRequired,
};
Upvotes: 0
Views: 766
Reputation: 1336
because useState is asynchronous function.
setCityData([response]); // asynchronous function so not set the new data to state yet.
console.tron.log(cityData);// so you get the old data.
See this Example
const Forecast = ({ idNavigation }) => {
const [cityData, setCityData] = React.useState([]);
function loadCityData(cityID) {
setTimeout(() => {
setCityData([1,2,3,4,5]);
console.log("this is old data", cityID, cityData); // because useState is asynchronous function
}, 2000);
}
React.useEffect(() => {
if (idNavigation) {
console.log("load ", idNavigation);
loadCityData(idNavigation);
}
}, [idNavigation]);
React.useEffect(() => {
if(cityData.length > 0) {
console.log("this is new data", cityData);
}
}, [cityData]); // when cityData changed and component mounted, this function called.
return <div>Forecast Weather</div>;
}
class App extends React.Component {
state = {
id: 1,
}
componentDidMount() {
setTimeout(() => {
this.setState({id: 2});
}, 2000);
}
render() {
return (
<div>
<Forecast idNavigation={this.state.id}/>
</div>
);
}
}
ReactDOM.render( <App / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Solution
if you use Class Component you can use callback of setState()
function.
but if you use Functional Component you can't use a callback.
so you should use useEffect()
to solve this problem.
Upvotes: 0
Reputation: 609
Umm, I think it maybe reactotron or api problem. Just try
const [cityData, setCityData] = useState('foo');
...
return <Text>{JSON.stringify(cityData)}</Text>;
If your plobrem came from reactron, then you can see the response from API.
Upvotes: 0
Reputation: 10873
Setting state in React is async for the most part and the changes to the state might not be visible if you try to console.log them right away. The recommended way to do this with hooks, is to check for the updated state in useEffect
:
async function loadCityData(cityID) {
const response = await api.get(`${cityID}`);
setCityData([response]);
}
// Track cityData and log the changes to it
useEffect(() => {
console.log(cityData);
}, [cityData]);
// Rest of the code
// ...
Upvotes: 1