How to fetch and manipulate data right when component mounts? - javascript

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

Related

How to fetch API as soon as page is loaded in React?

Whenever I visit a page it should automatically fetch the API
import React from 'react'
const Component = () => {
fetch("api url").then((res) => console.log(res))
return (
<div>comp</div>
)
}
export default Component
It is very simple using react hook use effect please learn basics of useffect hook on react docs or any youtube tutorial and as for the answer
import React, { useEffect } from 'react'
const comp = () => {
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, [])
return (
<div>comp</div>
)
}
export default comp
here empty dependency means every time page loads only once
use the useEffect for this.
The useEffect method will execute the passed callback on the mount of the component and on every time one of the dependency array parameters is changed. therefore:
const Comp = () => {
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, []);
return (
<div>comp</div>
)
}
Will make the callback to fire only once (because the empty dependency array) on the component mount.
You should use the useEffect Hook in your principal component like app.js
import React, {useEffect} from 'react'
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, []);
Be careful, this manipulation can consume a lot of resources (a lot of data to fetch etc.)
Thery
import React, { useState, useEffect } from 'react'
const Comp = () => {
const [ data, setData ] = useState([]);
const getData = async () => {
const res = await fetch("api url");
const data = await res.json();
setData(data)
}
useEffect(()=>{ getData() },[]);
return (
<>
<div>comp</div>
// dispaly your data here from data state
</>
)
}
export default Comp;
Fetch and use data with useState
const initialValue = {};
const comp = () => {
const [data, setData] = useState(initialValue);
useEffect(() => {
let ignore = false;
const fetchData = async () => {
const res = fetch("api url");
if (ignore) { return; }
setData(res.json())
return () => {
ignore = true;
}
}
, [])
return (
<div>comp {data.prop}</div>
)
}
More on working with state
More about useEffect life cycle
Hope it helps
You don't need to use the API function like this, it will be called continuously, you need to use useEffect hook, when your component reloads useEffect will be called, and you can learn about the useEffect dependency here,
import React, { useEffect, useState } from 'react'
const comp = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch("api url").then((res)=> {
console.log(res)
setData(res)
} )
}, [])
return (
// use data state to show the data here
<div>comp</div>
)
}
export default comp;

Fetching json api using react ( useeffect )

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();
}, []);

Why am i getting and empty array when fetching an api with react hooks?

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

API is getting called over and over again when I am using react hooks

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>
);
};

Using .map() with useEffect and Api

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))
},[]);

Categories