React update state variable with JSON data - javascript

App.js:
function App() {
const [items, setItems] = useState([]);
useEffect(() => {
const searchDB = () => {
fetch("http://127.0.0.1:8443/subColumns/5/?key=fc257229-8f91-4920-b71f-885403114b35", {
mode: 'cors',
credentials: 'include'
})
.then(res => res.json())
.then((json) => {
setItems(json);
})
console.log({items});
}
searchDB();
}, [])
I need to keep the json response in a state varibale because in the future, the API request will nt be hard coded and I expect the user will make multiple API requests without refreshing, and the results will have to be mapped to different components. At the moment, trying to print {items} to the console returns an empty array.

Since setItems is the asynchronous method, you can't get the updated value immediately after setItems. You should use another useEffect with dependency to see the value.
useEffect(() => {
console.log(items);
}, [items]);

Related

useEffect fetch request is pulling data twice

What I Want:
I'm pulling data from an api, then setting the data to state. I've done this inside a useEffect hook, but when I console.log the data afterwards, it's displaying the data twice, sometimes 4 times. I'm at a loss as to why this is happening.
What I've Tried:
console.log within useEffect to see data from source
disabling react developer tools within chrome.
My Code:
// make api call, assign response to data state
const [apiData, setApiData] = useState();
useEffect(() => {
async function fetchData() {
try {
await fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(data => setApiData(data));
} catch (e) {
console.error('Error fetching api data', e);
};
};
fetchData();
}, []);
console.log(apiData);
Result of console.log:
As was mentioned in the other comment this is due to effects being "double invoked" in strict-mode.
A common solution and one I believe has been suggested by the React team (although am struggling to find where I read this) is to use useRef.
// make api call, assign response to data state
const [apiData, setApiData] = useState();
const hasFetchedData = useRef(false);
useEffect(() => {
async function fetchData() {
try {
await fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(data => setApiData(data));
} catch (e) {
console.error('Error fetching api data', e);
};
};
if (hasFetchedData.current === false) {
fetchData();
hasFetchedData.current = true;
}
}, []);
If you are using React version 18+, StrictMode has a behavior change. Before it wasn't running effects twice, now it does to check for bugs. ( Actually, it does mount-remount, which causes initial render effects to fire twice)
https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors

How can I take the data to Array ReactJS?

I fetch data from an api and data takes successfully but there is a problem when I determine the data to an array.
Array doesn't assign the data.
const [Profile,setProfile]=useState([]);//I created array state here
const [username,setUsername]=useState('');
const [password,setPassword]=useState('');
const [logout,setLogout]=useState(0);
useEffect(() => {
return () => {
console.log(Profile);
}
}, [Profile.length])
const login = () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Access-Control-Allow-Origin","*");
myHeaders.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
var raw = JSON.stringify({username, password});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://localhost/scheduleapp/api/personals/login", requestOptions)
.then(response => response.text())
.then(response =>{setProfile(response) ;console.log(response); localStorage.setItem('Logged',response)})//here response is seen console and setted to local storage with success but setstate doesnt work
.catch(error => console.log('error', error));
}
As I say setstate doesnt set data to array
If you want to add an array to your state. First you can give that state an empty array as an initial value.
Example -
const [ data, setdata] = useState([])
After that you can destructor your response and assign them one by one to the array like this
setdata(... response)
Make sure the response is an array.
use response.json() instead of response.text() and for storing the response in the localStorage use localStorage.setItem(JSON.stringify(response))
The problem with the profile state, you need to renamed it to 'profile' instead of 'Profile' because React assuming is a component for the capitalization, that's why it never assigns the value to the state.
for Example:
const [profile,setProfile]=useState([]);
instead of this:
const [Profile,setProfile]=useState([]);
Other important detail is how you are using the 'useEffect' for example when you are console.log the results is when the component is unmounted, to make sure you are setting the value put it outside the useEffect to see if the state is changing like this:
useEffect(() => {
return () => {
console.log(Profile);
}
}, [Profile.length])
use like this:
useEffect(() => {
// call your function here if you want to execute when the component is mounted.
}, []);
console.log(profile);

Setting API data as state in React is undefined, but the API data exists

I have a weird issue with my React state. I'm fetching data from my NodeJS backend, and it comes back correctly in my frontend React app. But when I try to initialize a state with the data that was fetched, the state's value is "undefined", even though I know the data is coming back from the backend correctly.
here are the important parts of my react code:
const [currentCityData, setCurrentCityData] = useState({});
const fetchData = (cityValue) => {
axios.get('http://localhost:5000/get-weather', {
params: {
cityValue: cityValue
}
})
.then(res => console.log(res?.data?.data[0]))
.then((res) => setCurrentCityData(res?.data?.data[0]))
.then(console.log(currentCityData))
.catch(error => console.log(error))
};
useEffect(() => {
fetchData('toronto&country=canada');
}, []);
I'm getting the weather of a city. When I do a console log of the data I get back .then(res => console.log(res?.data?.data[0])) inside fetchData, the data is correct (its just an object with many weather properties). The line after that I set my state currentCityData, but then when I console log my currentCityData right after, its undefined.
Exactly what am I doing wrong here?
You are not returning aything from the first promise then handler. :
axios.get('http://localhost:5000/get-weather', {
params: {
cityValue: cityValue
}
})
.then(res => console.log(res?.data?.data[0])) // <--- here you should return
.then((res) => setCurrentCityData(res?.data?.data[0]))
.then(console.log(currentCityData))
.catch(error => console.log(error))
};
change your code to :
axios.get('http://localhost:5000/get-weather', {
params: {
cityValue: cityValue
}
})
.then(res => {console.log(res?.data?.data[0]); return res?.data?.data[0] }) // <--- here you should return
.then((res) => setCurrentCityData(res?.data?.data[0]))
.then(console.log(currentCityData))
.catch(error => console.log(error))
};
Demo : Demo

Setting state on array logs empty array multiple times

I am trying to fetch data from a backend server and hold it in an array. After I have done this I want to pass the array to another
component. Although, when I try and populate the array and pass it to my component, I get multiple empty arrays passed rather than an array with data.
I first initialise the state of the array using useState()
const [data, setData] = useState([]);
I then have a function that fetches data from the backend and attempts to populate data.
useEffect(() => {
const fetchData = () => {
fetch('/data')
.then((res) => res.json())
.then((data) => {
for (const property in data) {
setDailyCases([...dailyCases].push(`${data[property]}`));
}
});
}
fetchData();
},[])
When I pass this data to another component: <DataComp data={data}, I don't get the data I was expecting.
When I console.log(props.data) this is the output:
Which is strange beacuse If I console.log() while running the data loop all the data is visible:
How can I make sure the data array is updating correctly, and when passed I get one array of all the data?
Here is the DataComp component:
const DataComp = (props) => {
console.log(props.cases)
return (
<h1>Testing</h1>
)
}
export default DataComp
Using #Fardeen Panjwani answer my component is getting the correct data, although I am now getting more outputs to the console that expected?
You're never calling setData with the fetched data as parameter.
useEffect(() => {
const fetchData = () => {
fetch('/data')
.then((res) => res.json())
.then((_data) => { // using "_data" in order to avoid clash with the state-hook
setData(_data) // <= this line is responsible for populating the "data" value.
for (const property in _data) {
setDailyCases([...dailyCases].push(`${_data[property]}`));
}
});
}
fetchData();
},[])
In order to update data's value, you need to call the setData method.

React fetch data fires over and over again

Does anyone know why this fetch continues to fire. I have also tried putting it inside a useEffect with no luck. It should only fire once to return once imdbID has loaded.
const WatchOnList = ({ imdbId }) => {
const [locations, setLocations] = useState([])
var headers = new Headers();
headers.append("x-api-key", "API_KEY")
var requestOptions = {
method: 'GET',
headers: headers,
crossDomain: true,
redirect: 'follow'
};
async function fetchData() {
const res = await fetch(`${awsApiUrl}?imdb_id=${imdbId}`, requestOptions);
res
.json()
.then((res) => {
setLocations(res)
console.log(locations)
})
.catch(error => console.log('error', error));
}
fetchData();
With the current structure, the request will fire on every re-render. Which will be quite often in a React app. useEffect is the right place for such a function. But there are some caveats:
You can't make useEffect async, you have to create an async function inside the hook instead and call it afterward.
useEffect will per default run on every update, so you have to tell it explicitly to only run once (like componentDidMount for class components). This can be done by passing an empty array as the second parameter. The hook watches parameters specified in this array and only updates when one of them changes. As it is empty, it only fires once on initialization.
This should work:
useEffect(() => {
async function fetchData() {
const res = await fetch(`${awsApiUrl}?imdb_id=${imdbId}`, requestOptions);
res
.json()
.then(res => {
setLocations(res);
console.log(locations);
})
.catch(error => console.log("error", error));
}
fetchData();
}, []);
Read more about the behavior of hooks here and here.

Categories