Why does my data appear in the console 4 times? - javascript

I'm trying to get data from a weather api using React hooks. When I console.log the data after the useEffect hook it is printed to the console 4 times, twice as an empty object. Why is that? Here is my code:
import React, {useState, useEffect} from 'react';
export default function App() {
const [data, setData] = useState({})
useEffect(() => {
fetch('https://api.weatherbit.io/v2.0/forecast/hourly?city=Chicago,IL&key=XXX&hours=24')
.then(res => res.json())
.then(response => setData(response.data))
}, []);
console.log(data)
return (
<div>
</div>
)
}
and this is what is printed in the console:
{}
{}
(24) [{},{}...]
(24) [{},{}...]

One render happens with an empty object because the component mounts. Another happens with data because you set state. And then these are both doubled due to Strict Mode

Related

Variable in React is undefined on first render but defined after re-rendering. (Uncaught TypeError: vairable is undefined)

When I try to show the title of a movie (line 8 of App.js) I get the error "Uncaught TypeError: movies[0] is undefined". If I do a log of the variable "movies" after line 5 the console makes two logs: Frist, logs movies as an empty array and then logs movies as an array with all the content, like I want.
The weird thing is that if I delete the line 8 {movies[0].title} and save the error dissapears as normal, but then if I add it again and save the movie title renders on screen like I'm trying to do.
App.js:
import './App.css';
import useApi from './useApi';
function App() {
const movies = useApi();
return (
<div className="App">
{movies[0].title}
</div>
);
}
export default App;
My useApi returns an array with a list of movies:
import axios from 'axios';
import { useState, useEffect } from 'react';
const useApi = () => {
const [movies, setMovies] = useState([]);
const getData = async () => {
try {
const res = await axios(url);
setMovies(res.data.results);
} catch (error) {
return [];
}
}
useEffect(() => {
getData();
}, [])
return movies;
}
export default useApi;
Knowing that if I print movies it logs an empty array but then it prints it normal like I said, i guess I'm having a problem with asynchrony but I can't figure out what. And the fact that it works when removing and readding line 8 makes me more confused.
Because your getData() function render two times in useEffects hook. You need to check if(movies.length>0) and after that type return!
You may want to use the notion of conditional rendering. Something like this may work:
import './App.css';
import useApi from './useApi';
function App() {
const movies = useApi();
if (movies.length) { // conditionally render when the movie list is not empty
return (
<div className="App">
{movies[0].title}
</div>
);
}
return <div>Loading...</div>
}
export default App;

console.log on useState Hook array logs several times after GET request with Axios

I have this React Component
import React, { useState, useEffect } from 'react';
import axios from "axios";
import "../../css/driversStandings.css";
function DriversStandingsComponent() {
const [data, setData] = useState([]);
var row = 1;
useEffect(() => {
axios.get("http://localhost:4000/api/standings").then(res => {
const driversChampionshipData = res.data[0].DriversChampionship
setData(driversChampionshipData);
console.log(data)
})
});
return (
//Here I return a mdbootstrap table, mapping the data array
)
}
export default DriversStandingsComponent;
I don't really understand why this happens, and if it affects the server performance.
Any idea for solving this? I don't even know if it's an error itself 😅
useEffect is called every time a component rerenders. You sholud add empty dependency array, that way useEffect calls only when component is mounted, like this:
useEffect(() => {
axios.get("http://localhost:4000/api/standings").then(res => {
const driversChampionshipData = res.data[0].DriversChampionship
setData(driversChampionshipData);
console.log(data)
})
}, []);

React useEffect rendering more than once

I'm new in Hooks. I coded a project with componentDidMount.
Now I'm learning about hooks and rewriting this project with hooks. I want to fetch the data and print it on the console first.
However, it renders 3 times. It is probably because I used 2 setState in useEffect. However, in one of them I set the data to data array and in the other I keep the loading value for spinner control. How can I use useEffect like componentDidMount just one time to pull data and set my states?
When I write the console into useEffect, "React Hook useEffect has a missing dependency: 'data'." warning and returns an empty list.
Btw I deleted strictmode.
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
const { data } = await axios.get("/data/data.json");
setData(data);
setLoading(false);
};
fetchData();
}, []);
console.log(data);
return <div className="App">App</div>;
}
export default App;
The component re-renders normally after each state update.
Hence, in your example, it re-renders when the value of data is updated and again, when loading is updated.
Please check this demo to examine how the renderings occur after each state change:
Initial render
After loading is set to true
After data is fetched and set
After loading is set to false
I think having the data variable twice might be causing a conflict with the linter. You can rename your data coming from your API call to prevent the warning: "React Hook useEffect has a missing dependency: 'data'."
Your component will re-render on each state update, but the useEffect will only run when your dependencies change. Since they aren't going to change, the API call only happens once.
To prove it, you can move the console.log(result) in your useEffect and see it only logs once. However, make sure you call it on your result and not data, because the state won't be updated until the next render after calling setData.
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
const { data: result } = await axios.get("/data/data.json");
setData(result);
setLoading(false);
console.log(result); // runs once
};
fetchData();
}, [setData, setLoading]);
console.log(data); // runs 3 times
return <div className="App">App</div>;
}
export default App;

Displaying an array of objects in React native using map method

I have following component where I want to display data from an API, ShopScreen.js. I retrieve data with useEffect hook from API in service folder, and everything is ok, the json data is being loaded into data variable from useState hook, when I console.log it.
I have problem to the display data from this variable with map method. I get the error: Cannot read property 'map' of undefined. Can spot somebody where the problem is?
ShopScreen.js:
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import { fetchShops } from '../services/fetchShops';
const ShopsScreen = props => {
const [data, setShops] = useState({});
useEffect(() => {
fetchShops()
.then(response => response.json())
.then(data => setShops(data));
}, []);
return(
<View>
<Text>The Shops Screen!</Text>
{data.result.map(shop => {return (<Text>{shop.address}</Text>)})}
</View>
);
};
export default ShopsScreen;
My service for fetching data is fetchShops.js
export const fetchShops = () => {
const URL = `https://vilvumbiyl.execute-api.eu-west-1.amazonaws.com/Dev/store/MEBD/list`;
return fetch(URL)
}
useEffect without any params is equal to componentDidMount and for this reason, is called after the render.
So, the first time your jsx code is called, data.result.map is undefined and only after the re-render, do to the response of fetchShops(), it has a value.
You simply need to check the value like this:
data.result && data.result.map()
You can try:
const [data, setShops] = useState({result : {}});
or test data.result before use map on it.
ShopsScreen returns your view(JSX) before you get answer from your rest API. The result is null. You get the exception.

Can't update React state hook from useEffect

I want to update my state hook with useEffect. Somehow it gives me an error and brokes my app.
Console.log gives right answer in console but somehow react doesn't want to recognize setCountries as a state hook function. What I am doing wrong here?
import React, { useState, useEffect } from 'react'
import axios from 'axios'
var host = 'https://restcountries.eu/rest/v2/all'
const App = () => {
const { countries, setCountries } = useState([])
useEffect(() => {
axios
.get(host)
.then(response => {
console.log(response.data)
setCountries(response.data)
})
})
return (
<div>
</div>
)
}
export default App
And error I am getting in browser
Unhandled Rejection (TypeError): setCountries is not a function
error in console:
Uncaught (in promise) TypeError: setCountries is not a function
at App.js:14
Two things: useState returns an array with two elements, and an array does not have a countries or setCountries key to destructure for. You could do this:
const { 0: countries, 1: setCountries } = useState([]);
or just
const [countries, setCountries] = useState([]);
Secondly, useEffect(..) without any second argument will cause the effect to be triggered on every rerender, so your component will get stuck in an update loop. Use an empty array as its second argument to only trigger the effect on load.

Categories