Hellooo,
This is my first time using React. I want to fetch some data in json format and list it out in my page. The code below is not working.
import React, { useState, useEffect } from "react";
import axios from "axios";
function DataFetching() {
const [users, setUsers] = useState({ hits: [] });
//const [query, setQuery] = useState("redux");
useEffect(async () => {
const fetchData = async () => {
const result = await axios("url");
setUsers(result.data);
};
}, []);
return (
<div>
<p>Hellooooo</p>
<ul>
{users.hits.map((user) => (
<li key={user.id}>{user.title}</li>
))}
</ul>
</div>
);
}
export default DataFetching;
Issue
useEffect hook callbacks are 100% synchronous, they can't be asynchronous (i.e. declared async) at all. This also implicitly returns a Promise and messes with the effect cleanup functionality.
Your code never calls the fetchData function, so nothing is really updated.
You stomp your state shape and place the result array at the root state level instead of in an object under the hits key.
Solution
Remove the async keyword from the effect callback.
Invoke fetchData in the effect callback body.
Update state correctly.
Code:
useEffect(() => {
const fetchData = async () => {
try {
const result = await axios("url");
setUsers({ hits: result.data });
} catch(error) {
// handle any GET request or response handling errors
}
};
fetchData();
}, []);
Related
im having a little issue trying to fetch and filter some data when a component mounts. Basically what I am trying to do is to fetch some random data and then filter it with a condition. but the filtering of the data is not working, there is most likely a part I misunderstood using useEffect. I made a code sample where I simplified and replicated the issue on https://codesandbox.io/s/green-night-rhg4lj?file=/src/App.js
When I press on the button I expect the filtered data to be console logged, but gives me only an empty array, Ive tried to add "filteredData" or "fetchedData" as a dependency of the useEffect, and yes, it does help me getting the filtered data right at the start but goes into an endless loop because of the behaviour of the useEffect dependencies with obj and arrays. Anyone knows of a way to get the data from API/Database and filter it right on the mount without going into a fetch loop?
Here is also the code written beside the codesandbox:
import axios from "axios";
import { useState, useEffect, useCallback } from "react";
export default function App() {
const [fetchedData, setFetchedData] = useState([]);
const [filteredData, setFilteredData] = useState([]);
const getData = useCallback(async () => {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
setFetchedData(data);
}, []);
useEffect(() => {
getData();
(async () => {
await setFilteredData(fetchedData.filter((p) => p.title.length > 20));
})();
}, []);
const clickHandler = () => {
console.log(filteredData);
};
return (
<div className="App">
<button onClick={clickHandler}>Click</button>
</div>
);
}
You almost right!
You were right when putting the request in the useEffect hook.
...
const getData = useCallback(async () => {
const { data } = await axios.get("https://jsonplaceholder.typicode.com/posts");
return data
}, []);
useEffect(async () => {
const dataFromAPI = await getData();
setFilteredData(dataFromAPI.filter((p) => p.title.length > 20));
}, []);
...
Instead updating the state in the getData funtion just return it.
In the useEffect you get that data and the do what ever you want to do there.
note:
According to this it's ok to use async in useEffect
I have made two simple straight forward component is React, used a open source API to test API integration. React is showing this weird behavior of infinite console logs. I don't understand the issue. I'm using the fetch function for making API calls and functional component.
App component:
function App() {
const [characters, setCharac] = useState([])
const URL = "https://swapi.dev/api/";
fetch(URL + "people").then(response => response.json().then(data => {
setCharac(data.results)
console.log('Test');
}))
return (
<div className="App">
{characters.map(charac => {
return <Character {...charac} />
})}
</div>
);
}
Character component:
const Character = (props) => {
console.log(props);
return (
<div key={props.name}>
<h1>{props.name}</h1>
<p>{props.height}</p>
</div>
);
}
console.log('Test'); in App component and console.log(props); in Character component are being executed infinitely.
This is the render method
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Your components are rendering multiple times because your state is changed every time you fetch data (because of setState).
Try creating a function fetchData(). Make this function async as well to wait for data to be retrieved.
const fetchData = async () => {
const result = await fetch(URL + "people").then(response => response.json().then(data => {
setCharac(data.results)
console.log('Test');
return data;
}));
return result;
}
and then use it inside useEffect (Read more about useEffects: React hooks: What/Why `useEffect`?)
useEffect(() => {
fetchData();
}, []);
Note the usage of [] in useEffect. The data will be fetched only once when you load the component.
Try wrapping it in a useEffect
e.g.
useEffect(()=>{
fetch(URL + "people").then(response => response.json().then(data => {
setCharac(data.results)
console.log('Test');
}))
},[])
otherwise every time the state is set it is firing off the fetch again because a re-render is being triggered.
Because you fetch some data, update the state, which causes a re-render, which does another fetch, updates the state, which causes another render...etc.
Call your fetch function from inside a useEffect with an empty dependency array so that it only gets called once when the component is initially rendered.
Note 1: you can't immediately log the state after setting it as setting the state is an async process. You can, however, use another useEffect to watch for changes in the state, and log its updated value.
Note 2: I've used async/await in this example as the syntax is a little cleaner.
// Fetch the data and set the state
async function getData(endpoint) {
const json = await fetch(`${endpoint}/people`);
const data = await response.json();
setCharac(data.results);
}
// Call `getData` when the component initially renders
useEffect(() => {
const endpoint = 'https://swapi.dev/api';
getData(endpoint);
}, []);
// Watch for a change in the character state, and log it
useEffect(() => console.log(characters), [characters]);
You can do something like this:
import React, { useState, useEffect, useCallback } from "react";
const Character = (props) => {
console.log(props);
return (
<div key={props.name}>
<h1>{props.name}</h1>
<p>{props.height}</p>
</div>
);
};
export default function App() {
const [characters, setCharac] = useState([]);
const makeFetch = useCallback(() => {
const URL = "https://swapi.dev/api/";
fetch(URL + "people").then((response) =>
response.json().then((data) => {
setCharac(data.results);
console.log("Test");
})
);
}, []);
useEffect(() => {
makeFetch();
}, []);
return (
<div className="App">
{characters.map((charac) => {
return <Character {...charac} />;
})}
</div>
);
}
I am new with react hooks, i'm trying to get info from an API but when i do the request i get 2 responses first an empty array and then the data of the API, why am i getting that empty array! , this is my first question, i'm sorry.
Thanks for helping me !
import {useState, useEffect} from 'react';
const getSlides = (API) => {
const[data,setData] = useState([]);
const getData = () =>
fetch(`${API}`)
.then((res) => res.json())
useEffect(() => {
getData().then((data) => setData(data))
},[])
return data
}
export default getSlides;
The useEffect() hook runs after the first render. Since you've initialized the data state with an empty array, the first render returns an empty array.
If you're component depends on data to render, you can always conditionally return null until your data is loaded.
Also, I recommend using an async function for api requests, it allows you to use the await keyword which makes your code easier to read. The only caveat, is that you cannot pass an async function to useEffect, instead define an async function inside your hook, and then call it.
import React, { useState, useEffect } from "react";
const API = "https://example.com/data";
const GetSlides = (props) => {
const [data, setData] = useState();
useEffect(() => {
async function getData() {
const request = fetch(API);
const response = await request;
const parsed = await response.json();
setData(parsed);
}
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (data === undefined) {
return null;
}
return <>data</>;
};
export default GetSlides;
Of course, you can still use Promise chaining if you desire.
useEffect(() => {
async function getData() {
await fetch(API)
.then((res) => res.json())
.then((data) => setData(data));
}
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
<GetSlides api="https://yay.com" />
react components need to be title case
import React, { useState, useEffect } from 'react'
const GetSlides = ({ api }) => {
const [data, setData] = useState(null)
const getData = async () =>
await fetch(`${api}`)
.then((res) => res.json())
.then((data) => setData(data))
useEffect(() => {
getData()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
console.log(data)
return <div>slides</div>
}
export default GetSlides
The effect callback function is called after the render of your component. (Just like componentDidMount) So during the first render phase, the data state has not been set yet.
You initialize your data with and empty array here:
const[data,setData] = useState([] <- empty array);
useEffect runs after your component is mounted, and then calls the API, that it might take a few seconds or minutes to retrieve the data, but you return the data right away before knowing if the API finished its call.
If you want to return the data after it has been retrieved from the API, you should declare and async method
const getSlides = async (API) => {
try {
const res = await fetch(API);
const data = await res.json();
return data;
} catch (e) {
throw new Error(e);
}
}
Note that it is not necessary hooks for this function
I am using React Hooks to get data from an existing API. Here is my code
import React, { useState, useEffect } from "react";
export const People = () => {
const [people, setPeople] = useState([]);
const url = "http://127.0.0.1:5000/people";
async function fetchData() {
console.log("calling api .. ");
const res = await fetch(url);
res.json().then((res) => setPeople(res));
}
useEffect(() => {
fetchData();
});
return (
<div>
<ul>
{people &&
people.map((person) => <li key={person.id}>{person.name}</li>)}
</ul>
</div>
);
};
const Dashboard = (props) => {
return (
<div>
<People />
</div>
);
};
export default Dashboard;
The problem that I am having is this API is getting called over and over again. Can you please let me know what I am doing wrong here.
Thanks
Currently, using useEffect(callback) will execute the callback on every render.
Try using useEffect with an empty dependencies array.
If you want to run an effect and clean it up only once, you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.
Check my other answer on useEffect use cases.
useEffect(() => {
fetchData();
}, []);
As for the rest of the component, it should look like so (opinioned):
// Should be on the outer scope as the URL is decoupled from the component's logic
const url = "http://127.0.0.1:5000/people";
export const People = () => {
const [people, setPeople] = useState([]);
useEffect(() => {
// This way fetchData won't re-assigned on every render
async function fetchData() {
console.log("calling api .. ");
const res = await fetch(URL);
// Consitstance, don't mix `then` and `await` syntax
const people = await res.json();
setPeople(people);
}
fetchData();
}, []);
return (
<div>
<ul>
{people &&
people.map((person) => <li key={person.id}>{person.name}</li>)}
</ul>
</div>
);
};
I am trying to use the useEffect to grab some data from an API. I am succesful in grabbing the data but after I set my state and try to map through it I just get "Can't read map of undefined". I think the problem is that it's running my .map() code before it gets the response. i am just unsure of how to solve this
This is the api response:
data: {count: 87, next: "https://swapi.co/api/people/?page=2", previous: null, results: Array(10)}
Here is my code
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
import CharacterMap from './characterMap'
const App = () => {
let [getChars, setChars] = useState(0);
useEffect(() => {
axios.get(`https://swapi.co/api/people/`)
.then(res => setChars(res) )
},[]);
console.log(getChars.data.map((e) => e))
return (
<div className="App">
<CharacterMap info={getChars} />
</div>
);
}
export default App;
axios.get is an async function and you are trying to get the data outside of an async function which is no completed yet.
You could use useEffect with dependency array which is equal to componentDidUpdate to get the data.
Initialized the state with the same datatype that you expect, in this case we expect an array you initialized ith with empty array.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
import CharacterMap from './characterMap'
const App = () => {
let [chars, setChars] = useState([]);
useEffect(async () => {
try{
let response = await axios.get(`https://swapi.co/api/people/`)
let data = await response.json();
setChars(data);
} catch(error) {
console.error(error.message);
}
},[]);
// If you want to access the updated state then use this.
useEffect(() => {
let newState = chars.map((e) => e); // map your state here
setChars(newState); // and then update the state
console.log(newState);
},[getChars]);
return (
<div className="App">
<CharacterMap info={chars} />
</div>
);
}
export default App;
The second useEffect hook trigger on each state update and so you can get the updated state here.
It will also trigger a re-render so you can also use the map in return statement;
Or you could update the data on axios response and then set the state. Recommended
useEffect(async () => {
try{
let response = await axios.get(`https://swapi.co/api/people/`)
let data = await response.json();
let newState = data.map((e) => e); // map your state here
setChars(newState); // and then update the state
console.log(newState);
} catch(error) {
console.error(error.message);
}
},[]);
Keep the default values as array
let [getChars, setChars] = useState([]);
you are setting data to array chars. instead of that set array(results) that you are getting in response.
As you defined let [getChars, setChars] = useState([]);
useEffect(async () => {
axios
.get(`https://swapi.co/api/people/`)
.then(res=> setChars(res.data.results))
.catch(err=> console.log(err))
},[]);