UseEffect Infinite Loop , with axios - reactjs

Hi i am using Useeffect in reactt with axios to display objects from the database ,
I faced an infinite loop in the useeffect which I cannot display the objects on the browser a lot of time ,
but spring boot server still infinite call and also on the console there is an infinite objects displayed when I print the data from useeffect.
here is my code and hope u can suggest a solution for the prob;em
const [filterKeywords, setfilterKeywords] = useState([]);
const [companies, setCompanies] = useState([]);
useEffect(() => {
axios.get(`/api/users/allCompanies`)
.then(res => {
setCompanies(res.data);
})
});
and I pass them to companies component
<Companies
keywords={filterKeywords}
data={companies}
setKeywords={addFilterKeywords}
/>
then pass them to company component
return (
<div className="jobs">
{(filteredData.map(d => {
return <Company key={d.id} data={d} setkeywords={setKeywords} />
}))}
</div>
)

Change your useEffect code to this.
useEffect(() => {
axios.get(`/api/users/allCompanies`)
.then(res => {
setCompanies(res.data);
})
},[]);

Add Empty dependencies ... like this at the end
useEffect(() => {
axios.get(`/api/users/allCompanies`)
.then(res => {
setCompanies(res.data);
})
},[]);

You do not have any callback in your useEffect that's why it goes to do infinity.
You can add array like this [] or you can add callback trigger in your array then you will not have that problem anymore
useEffect(() => {
axios.get(`/api/users/allCompanies`)
.then(res => {
setCompanies(res.data);
})
},[]);

Here is what you need to do.
const [filterKeywords, setfilterKeywords] = useState([]);
const [companies, setCompanies] = useState([]);
useEffect(async () => {
await axios.get(`/api/users/allCompanies`)
.then(res => {
setCompanies(res.data);
})
},[]);
When you are calling an api you should use async await, but that not the cause of the inifinity loop you was missing []
Option 2:
const [filterKeywords, setfilterKeywords] = useState([]);
const [companies, setCompanies] = useState([]);
useEffect(() => {
(async ()=>{
const {data} = await axios.get(`/api/users/allCompanies`)
setCompanies(data);
})();
},[]);

Related

React.js with fetch api and console.log()

I can't log or display data in this barebones React component. I am trying to just simply fetch my repos using the fetch api. I am getting a response back when I look at Network tab in dev tools.
I tried to wrap the call in useEffect() (then storing the data from the response into a state variable) - that didn't work so that's why I have this barebones component for now.
const Component = () => {
const [repos, setRepos] = useState([])
useEffect(() => {
// fetch call used to be here
}, [])
const data = fetch('https://api.github.com/users/alexspurlock25/repos')
.then(response => response.json())
.then(data => setRepos(data))
console.log(data)
console.log(repos)
return (
<div>
{
repos.map(items => console.log(items))
}
</div>
)
}
Why can't I log or map the data? Am I doing something wrong?
Create an async function that handles the api call. Then call the function in the useEffect. Since Repos is an empty array, nothing will be logged. Once your api call resolves and the repos state has been updated, react will do it's thing and re render causing the repos.map to run again and log out the repos
const Component = () => {
const [repos, setRepos] = useState([])
const fetchData = async ()=>{
let res = await fetch('https://api.github.com/users/alexspurlock25/repos')
let data = await res.json()
setRepos(data)
}
useEffect(() => {
// fetch call used to be here
fetchData()
}, [])
return (
<div>
{
repos.map(items => console.log(items))
}
</div>
)
}
You have to verify that the repos are defined and contain data to do that you can do the following
//mock up API
const API = (ms = 800) => new Promise(resolve => setTimeout(resolve, ms, {state:200, data:[1,2,3,5]}));
ReactDOM.render(
<App />,
document.body
);
function App(props){
const [repos, setRepos] = React.useState([]);
React.useEffect(() => {
API()
.then(res => setRepos(res.data));
},[])
return <div>{
// Check Here
repos.length > 1 ? repos.map((r,i) => <div key={`${r}-${i}`}>{r}</div>) : <div>Loading ...</div>
}</div>
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
const Component = () => {
const [repos, setRepos] = useState([])
useEffect(() => {
const data = fetch('https://api.github.com/users/alexspurlock25/repos')
.then(response => response.json())
.then(data => setRepos(data))
console.log(data)
}, [])
console.log(repos)
return (
<div>
{
repos.map(items => console.log(items))
}
</div>
)
}

React dependency warning from console log when using useEffect

const [order, setOrder] = useState({});
useEffect(() => {
fetch(`https://localhost/services/${id}`)
.then(res => res.json())
.then(data => setOrder(data))
}, [])
You need to add id to the dependency array
const [order, setOrder] = useState({});
useEffect(() => {
fetch(`https://localhost/services/${id}`)
.then(res => res.json())
.then(data => setOrder(data))
}, [ id ])
I wouldn't worry too much about the dependency warning, if you only want this to run when the component mounts then just ignore it, or turn it off.
Alternatively you can create your own effect called useMount that can act as a pseudo hook for when you only want things to run once, for example.
import { useEffect } from 'react';
/**
* This is a pseudo hook that is designed to navigate around the react-hooks/exhaustive-deps rules in eslint without the need to
* disable it entirely in the rest of the application. useMount guarantees that it will only run once when mounted, and
* only requires the rules disabling for this definition.
*
* Use `useMount` in every place you would normally call `useEffect` with the intention of running it once.
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
export const useMount = (mount: () => void) => useEffect(mount, []);
export default useMount;
Extra http request from useEffect:
const fetchData = () => {
fetch(`https://localhost/services/${id}`)
.then(res => res.json())
.then(data => setOrder(data))
}
useEffect(() => {
fetchData()
}, [])
Try this way , hope it will work
useEffect(() => {
const fetchData = () => {
fetch(`https://localhost/services/${id}`)
.then(res => res.json())
.then(data => setOrder(data))
};
fetchData ();
}, []);

useEffect infinite loop network request

I am getting infinite requests on my network, and it's due to my useEffect. I know that the problem is because I am putting in the brackets as the second argument the 'posts' and the 'setPost' inside the useEffect function, but I need the page to render whenever I add a new post, so the 'posts' must be inside the brackets.
function Home() {
const {userData, setUserData} = useContext(userContext)
const [posts, setPost] = useState([])
const [createPost, setCreatePost] = useState('')
const handleToken = () => {
localStorage.removeItem('auth-token')
}
const token = localStorage.getItem("auth-token");
const handleOnSubmit = (e) => {
e.preventDefault()
axios.post('http://localhost:5000/posts', {textOfThePost: createPost}, {
headers: { 'auth-token': token },
})
.then((res) => {setCreatePost("")})
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}
useEffect(() => {
}, [posts])
If you're doing setPost inside useEffect, I assume posts being changed, and you've added posts as dependency in useEffect, Of course which will re-call and it goes infinite loop. Make sure when do you want to call posts API.
const [posts, setPost] = useState([])
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data) // Which will change `posts`
})
}, [posts]) // this will trigger useEffect and It goes infinite loop
// Change it to
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data) // Which will change `posts`
})
}, []) -> Which call only one time
This useEffects gets called everytime posts changes, and inside the useEffect you're changing posts value, so you got into an recursive loop.
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [posts])
If you want it to get called only once, you should leave the empty array in your effect, so it will only get called once when your component is mounted.
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [])

React useEffect gets data from database but not in time to be used in the component

I am using useEffect to get data from an api.
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const cats = await response.json();
console.log(cats);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
The problem is when I try to use it in the return, its value is undefined.
{cats.map((data) => {
cats has value when I console.log it.
I cannot use componentDidMount because all my code is functional components.
Edit: I updated the code as per answers below but still get
TypeError: cats.map is not a function
All answers below actually make sense but I am not sure why its not working.
export default function Posts() {
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(setCats)
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
<h4>{data.main}</h4>
})}
</div>
)
}
This is because React renders your screen before finishing to get response from API. When you render screen, variable cats doesn't have values. You can run useEffect after each rendering. You can rerender by changing state from useEffect (This technique is often used). Do not forget to add [] or [cats] as a dependency of useEffect (second params) otherwise you will get infinite loop.
Below code works even when cats === [] or some array.
export default () => {
const [cats, setCats] = useState([])
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const result = await response.json();
setCats(result)
} catch (e) {
}
};
fetchData();
}, []);
return (
<div>
{cats.map(cat => <div>cat</div>)}
</div>)
}
You have to map the cats data into state.
const [cats, setCats] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const data = await response.json();
setCats(data);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
You need to
call setCats when the response comes back (right now, you're just logging it)
.map only once cats has been populated:
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(result => setCats(result.cats))
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
// ...

Hooks in react are not updating my state correctly

I'm trying to load some data which I get from an API in a form, but I seem to be doing something wrong with my state hook.
In the code below I'm using hooks to define an employee and employeeId.
After that I'm trying to use useEffect to mimic the componentDidMount function from a class component.
Once in here I check if there are params in the url and I update the employeeId state with setEmployeeId(props.match.params.employeeId).
The issue is, my state value didn't update and my whole flow collapses.
Try to keep in mind that I rather use function components for this.
export default function EmployeeDetail(props) {
const [employeeId, setEmployeeId] = useState<number>(-1);
const [isLoading, setIsLoading] = useState(false);
const [employee, setEmployee] = useState<IEmployee>();
useEffect(() => componentDidMount(), []);
const componentDidMount = () => {
// --> I get the correct id from the params
if (props.match.params && props.match.params.employeeId) {
setEmployeeId(props.match.params.employeeId)
}
// This remains -1, while it should be the params.employeeId
if (employeeId) {
getEmployee();
}
}
const getEmployee = () => {
setIsLoading(true);
EmployeeService.getEmployee(employeeId) // --> This will return an invalid employee
.then((response) => setEmployee(response.data))
.catch((err: any) => console.log(err))
.finally(() => setIsLoading(false))
}
return (
<div>
...
</div>
)
}
The new value from setEmployeeId will be available probably in the next render.
The code you're running is part of the same render so the value won't be set yet.
Since you're in the same function, use the value you already have: props.match.params.employeeId.
Remember, when you call set* you're instructing React to queue an update. The update may happen when React decides.
If you'd prefer your getEmployee to only run once currentEmployeeId changes, consider putting that in its own effect:
useEffect(() => {
getEmployee(currentEmployeeId);
}, [currentEmployeeId])
The problem seems to be that you are trying to use the "updated" state before it is updated. I suggest you to use something like
export default function EmployeeDetail(props) {
const [employeeId, setEmployeeId] = useState<number>(-1);
const [isLoading, setIsLoading] = useState(false);
const [employee, setEmployee] = useState<IEmployee>();
useEffect(() => componentDidMount(), []);
const componentDidMount = () => {
// --> I get the correct id from the params
let currentEmployeeId
if (props.match.params && props.match.params.employeeId) {
currentEmployeeId = props.match.params.employeeId
setEmployeeId(currentEmployeeId)
}
// This was remaining -1, because state wasn't updated
if (currentEmployeeId) {
getEmployee(currentEmployeeId);
//It's a good practice to only change the value gotten from a
//function by changing its parameter
}
}
const getEmployee = (id: number) => {
setIsLoading(true);
EmployeeService.getEmployee(id)
.then((response) => setEmployee(response.data))
.catch((err: any) => console.log(err))
.finally(() => setIsLoading(false))
}
return (
<div>
...
</div>
)
}
The function returned from useEffect will be called on onmount. Since you're using implicit return, that's what happens in your case. If you need it to be called on mount, you need to call it instead of returning.
Edit: since you also set employee id, you need to track in the dependency array. This is due to the fact that setting state is async in React and the updated state value will be available only on the next render.
useEffect(() => {
componentDidMount()
}, [employeeId]);
An alternative would be to use the data from props directly in the getEmployee method:
useEffect(() => {
componentDidMount()
}, []);
const componentDidMount = () => {
if (props.match.params && props.match.params.employeeId) {
setEmployeeId(props.match.params.employeeId)
getEmployee(props.match.params.employeeId);
}
}
const getEmployee = (employeeId) => {
setIsLoading(true);
EmployeeService.getEmployee(employeeId);
.then((response) => setEmployee(response.data))
.catch((err: any) => console.log(err))
.finally(() => setIsLoading(false))
}

Resources