React Native, problem while updating State inside useEffect from async function - reactjs

I'm trying the fetch data from a json API and setting it to a state. Currently using visual studio code with a pixel 4 emulator.
When I try to update my state inside of a useEffect method on the emulator's first launch or on reload, it doesn't change. If I save in vs code, the data in state updates as intended.
...
import React, {useState, useEffect} from 'react';
import {getJsonData} from './getJsonData';
const myApp = () => {
const [state, setState] = useState({
isLoading: true,
data: null,
});
const updateState = data => {
console.log(data); //Logs the correct Json data everytime
setState(state => ({...state, isLoading: false, data: data}));
console.log(state.isLoading); //Doesn't update on reload (updates only on save)
console.log(state.data); //Same as above
};
useEffect(() => {
getJsonData().then(data => updateState(data));
}, []);
return (
<View>
<Text>{state.data.title}</Text>
<Text>{data.data.completed}</Text>
</View>
);
}
And this is the getJsonData( ) function:
export async function getJsonData() {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let responseJson = await response.json();
return responseJson;
} catch (error) {
console.error(error);
}
}
I ultimately want the state to update on the application's first run, on reload, as well as each time I call a certain reloadApp( ) function.
If the above code is not the best practice, please don't hold back to correct me as I'm just learning about states.

setState function is asynchronous. So console.log immediately after setState will give old value but not the new value.
Also why don't you seperate the states like
const [isLoading,setIsLoading]=useState(true);
const [data,setData] = useState(null);
and set them separately so your code looks better.
in updateState(jsonData) you can do then
setIsloading(false);
setData(jsonData);

Related

Add object array to setState

I'm trying to add an object array in the state series. With this code the useEffect function get stuck in an infinite loop. How can I solve this? Without adding the series const as parameter I get the error about a missing dependency and the code will only run on startup.
import React, { useState, useEffect } from "react";
const LineChart = () => {
const [series, setSeries] = useState([]);
useEffect(() => {
const url = "http://localhost:4000";
const fetchData = async () => {
try {
fetch(url, {
method: "GET",
})
.then((response) => response.json())
.then((data) => {
let chartData = data.testRunSummaries
.map(function (testrun) {
return {
duration: testrun.endTime - testrun.startTime,
label: testrun.testSetName + "#" + testrun.userFacingId,
testrun: testrun.testRunId,
status: testrun.status,
};
});
setSeries(chartData, ...series);
console.log(series);
});
} catch (error) {
console.log(error);
}
};
fetchData();
}, [series]);
return (
...
);
};
export default LineChart;
series is in your useEffect dependency array. And your useEffect is changing series. So obviously you'll be stuck in a infinite loop.
You don't need your series to be set as a dependency for useEffect.
As your useEffect will only be trigger once on mount, you can just have
setSeries(chartData);
And if you really need to have former values of series, you should use
setSeries(series => [...chartData, ...series]);
Moreover, seeing your
setSeries(chartData, ...series);
console.log(series);
Let me remind you that setState is async there is no way this will log your updated state :)

Data won't console.log in async (React Native)

I am using the library API here to basically fetch the title of books.
Before I move on, I need to atleast check and see if I am properly pulling out the data. So on my code:
async fetchRandomBooks() {
try {
let response = await fetch(
'http://openlibrary.org/search.json?author=tolkien',
);
let json = await response.json();
console.log(json);
this.setState({books: json, isFetching: false});
} catch (error) {
this.setState({errorMessage: error});
}
}
componentDidMount() {
this.fetchRandomBooks();
console.log(this.state.books);
}
I tried console.log inside the fetchRandomBooks as well as inside the componentDidMount but it did not gave me any data. As far as I remember you need to pick up on response.docs in order to pull something from the api.
Any idea how can I properly console.log the data and probably return something from it? I just want to make sure I am pulling out the data before proceeding.
You can not access console.log(this.state.books); in componentDidMount() because the API call is ongoing and it's taking a bit of time for complaining about it.
You can also try with react hooks.
import React from "react";
function App() {
const [fetching, setFetching] = React.useState([]);
const [data, setData] = React.useState([]);
const fetchRandomBooks = async () => {
setFetching(true);
try {
let response = await fetch(
"http://openlibrary.org/search.json?author=tolkien"
);
const json = await response.json();
console.log(json.docs);
setData(json.docs);
} catch (error) {
console.log(error);
} finally {
setFetching(false);
}
};
React.useEffect(() => {
fetchRandomBooks();
}, []);
return (
<div>
{fetching && <p>Loading...</p>}
<ul>
{data?.map((item) => {
return <li>{item.title}</li>;
})}
</ul>
</div>
);
}
export default App;
You could try the componentDidUpdate lifecycle method. It gets invoked immediately after an update to the component, may that be new props or an update to state.
Your code could look something like this:
componentDidUpdate(prevProps, prevState, snapshot) {
console.log(this.state.books)
}

Set state function is not properly updating the state

const component = () => {
const [data, setData] = useState(null);
const fetchData = async () => {
try {
new DataService().getData().then((response) => {
setData(response);
console.log(data);
console.log(response);
}
} catch (error) {
console.log(error);
}
}
useEffect(() => {
fetchData();
}, []);
}
Why does console.log(data) display null even though console.log(response) displays the correct data? The data state should've been set before I console.log'd it, no?
The data state should've been set before I console.log'd it, no?
No.
This is most likely because setData is asynchronous. Since you're immediately asking for the value of data after using setData it's likely that the value hasn't had the chance to update yet.
If you're a Chrome user, have you used React Developer Tools? This will allow you to check the state of a component without needing to rely on console.log.

How to setstate after fetch data React hook [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 10 months ago.
Code :
Result : Not have data in state
help me pls , thanks!
setState is asynchronous that's why you are seeing books as empty array. Here is a quote from the React docs:
The setState function is used to update the state. It accepts a new
state value and enqueues a re-render of the component.
One thing you may be doing wrong is in your useEffect callback. If your effect returns a function, React will run it when it is time to clean up. And you don't want the setState functions in fetchData to be invoked during clean up as the component will probably be unmounted.
If you just want the fetchData to only run once after the component mounts, here is a possible solution:
useEffect(() => {
// put the fetchData inside the effect
async function fetchData() {
setLoading(true);
const name = await getNameGroup();
const tmp = await getAll(name);
console.log(tmp);
setBooks(tmp);
console.log(books); // may not be the same as tmp, but you will see the updated state in the next render
setLoading(false);
}
fetchData();
},[]}
You should read more about useEffect hook in the React docs.
It's a stale closure problem.
Your useEffect where the fetchData is being called, has an empty dependency array. Within the fetchData function, which is inside useEffect, you are trying to print books which one first load, was initialized with an empty array.
All hooks hold the same reference to the variables with which they were initialized, till the dependencies change. To get an updated state, they depend on the dependency array. Since your dependency array doesn't specify books, it won't refresh the reference of books in your fetchData function either. Read more about the stale closure problem here
That's why your books variable is showing stale data.
export default function() {
// fetch data here
// runs only once because of empty dependency array
useEffect(() => {
let isCancelled = false
// define the fetchData inside the `useEffect` so that
// you can detect if the component has been unmounted
// using `isCancelled`
const fetchData = async () => {
const tmp = await getAll()
// only update state if component isn't unmounted
// if you try to update state on an unmounted component,
// React will throw an error
if (!isCancelled) {
setIsLoading(false)
setBooks(tmp)
}
}
if (!isCancelled) {
setIsLoading(true)
fetchData()
}
// cleanup
return () => {
isCancelled = true
}
}, [])
}
const [dataArray, setDataArray] = useState([]);
async function fetchData() {
try {
setIsLoading(true);
const response = await getNameGroup();
setDataArray(response);
} catch(error) {
// handle error
} finally {
setIsLoading(false);
}
}
This is an example code that is working and you can apply:
const [data, setData] = useState([]);
const [hasError, setErrors] = useState(false);
async function fetchData() {
const LibraryQuery = JSON.stringify({query: `query { species { id name description } }`});
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const res = await fetch('http://localhost:3000/graphql',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token
},
body: LibraryQuery
});
res
.json()
.then(res => setData(res.data))
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
}, []);

How to load axios data as initial redux state

I try to load some axios json result on my initial state in order to open my app with a prepopulated state but i do not manage to load the axios result in my that initial state, i can see it on my console but the return doesnt work here
this is the code of my reducer
import axios from "axios";
const getBookings = () => {
return new Promise((resolve) => {
axios.get('http://localhost:4000/bookings.json')
.then(res => resolve(res.data))
});
}
const getInitiatState = getBookings().then(
function(data)
{
console.log(data)
const initialState = {
data: data, // ' ' or axios result
};
return initialState;
}
)
function bookings(state = getInitiatState, action)
{
switch(action.type)
{
default:
return state
}
}
export default bookings;
As i said in comments: You should make it as empty array/object and "initialize" state later with proper action. Right now instead of making state with array you fill it with promise.
My sample using React with hooks and setTimeout (this will work the same with your fetch): https://codesandbox.io/s/6wwy4xxwwr?fontsize=14
You can also just do it in your "index.js" using store.dispatch(action) like:
import store from './fileWithConstStore';
fetch()
.then(data => {
store.dispatch({
type: 'INIT_BOOKINGS',
payload: data
})
});
but this rather quick than approved solution.

Resources