React - useEffect based on another useEffect - reactjs

I'm trying to understand how useEffect works.
I have two callApi: "callApiDialer" is based on response of "callApiManager", for get id from list.
But "currentLeadId" state at first called obviously is null.
How can call "callApiDialer" when currentLeadId is not null?
import React, { useState, useEffect } from 'react';
const [loading, setLoading] = useState(true);
const [apiManager, setApiManager] = useState([]);
const [apiDialer, setApiDialer] = useState([]);
const [currentLeadId, setCurrentLeadId] = useState(null);
// CALL API
const callApiManager = async () => {
try {
const response = await api.get(`/api/manager/op/1`);
setCurrentLeadId(response.data.dialer_list[0].id);
setApiManager(response.data);
} catch (err) {
alert("fetchApiManager " + err.response.status);
}
}
const callApiDialer = async () => {
try {
const response = await api.get(`/api/manager/lead/${currentLeadId}`);
setApiDialer(response.data.lead);
setLoadingModal(false);
} catch (err) {
alert("fetchApiSources " + err.response.status);
}
}
useEffect(() => {
callApiManager();
}, [])
useEffect(() => {
console.log(currentLeadId); // <-- here get first null and after currentLeadId
if(currentLeadId) {
callApiDialer();
setLoading(false);
}
}, [currentLeadId])

You could have just one function that call both, therefore there would be only one useEffect.
// CALL API
const callBothApisAtOnce= async () => {
try {
const op = await api.get(`/api/manager/op/1`);
const response = await api.get(`/api/manager/lead/${op.data.dialer_list[0].id}`);
// rest of your logic...
} catch (err) {
alert("err" + err);
}
}
useEffect(() => {
callBothApisAtOnce()
}, [])

you can use axios's promise base functionality
axios.get(`/api/manager/op/1`).then(res => {
setCurrentLeadId(response.data.dialer_list[0].id);
setApiManager(response.data);
axios.get(`/api/manager/lead/${response.data.dialer_list[0].id}`).then(res1 =>{
setApiDialer(res1.data.lead);
setLoadingModal(false);
}
}

Related

How to properly unmount a form in useEffect cleanup function [duplicate]

How to clean up react request in react hooks. I read that in need to enter in my hook AbortController but I don't know how. I using next.js. What are best methods to eliminate this problem ? And I get this warning:
Warning: can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
This is my custom hook to fetch data:
import { useState, useEffect, useCallback } from 'react'
import { MOVIE_API_URL, MOVIE_KEY } from '../../config'
export const useMovieDetailsFetch = (movieId) => {
const [state, setState] = useState({})
const [loading, setLoading] = useState(true)
const [error, setError] = useState(false)
const fetchData = useCallback(async () => {
setError(false)
setLoading(true)
try {
const movieDetailsEndpoint = `${MOVIE_API_URL}movie/${movieId}?api_key=${MOVIE_KEY}`
const result = await (await fetch(movieDetailsEndpoint)).json()
const creditsEndpoint = `${MOVIE_API_URL}movie/${movieId}/credits?api_key=${MOVIE_KEY}`
const creditsResult = await (await fetch(creditsEndpoint)).json()
// Filtring in crew for directors only
const movieDirectors = creditsResult.crew.filter(
(member) => member.job === 'Director'
)
setState({
...result,
movieDirectors,
actors: creditsResult.cast,
})
} catch (error) {
setError(true)
}
setLoading(false)
}, [movieId])
useEffect(() => {
fetchData()
}, [fetchData])
return [state, loading, error]
}
Using an abort controller, in its rawest form:
const controller = new AbortController();
const { signal } = controller;
...
fetch(url, { signal });
...
// abort
controller.abort();
To abort an in-flight fetch in effect hook
useEffect(() => {
const controller = new AbortController();
const { signal } = controller;
fetch(url, { signal });
return () => {
controller.abort(); // abort on unmount for cleanup
};
}, []);
I found this article very informative when I needed to develop a way to cancel fetch requests.
Edit
The signal needs to be added to the fetch requests options object. You can also define the async fetchData function inside the effect (this is normal), so it's all enclosed in the effect hook's callback scope.
export const useMovieDetailsFetch = (movieId) => {
const [state, setState] = useState({})
const [loading, setLoading] = useState(true)
const [error, setError] = useState(false)
useEffect(() => {
const controller = new AbortController();
const { signal } = controller;
const fetchData = async () => {
setError(false);
setLoading(true);
try {
const movieDetailsEndpoint = `${MOVIE_API_URL}movie/${movieId}?api_key=${MOVIE_KEY}`;
const result = await (
await fetch(movieDetailsEndpoint, { signal })
).json();
const creditsEndpoint = `${MOVIE_API_URL}movie/${movieId}/credits?api_key=${MOVIE_KEY}`;
const creditsResult = await (
await fetch(creditsEndpoint, { signal })
).json();
// Filtring in crew for directors only
const movieDirectors = creditsResult.crew.filter(
(member) => member.job === 'Director'
);
setState({
...result,
movieDirectors,
actors: creditsResult.cast,
});
} catch (error) {
setError(true);
}
setLoading(false);
}
fetchData();
return () => controller.abort();
}, [movieId]);
return [state, loading, error];
}

React Native AsyncStorage getAllKeys infinite loop

This AsyncStorageHooks react custom hook
import {useState} from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
const AsyncStorageHooks = (key, value, mergedValue, keys) => {
const [data, setData] = useState('');
const [error, setError] = useState('');
const storeData = async () => {
try {
if (key && value) {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(key, jsonValue);
setData(jsonValue);
}
} catch (e) {
setError(e);
}
};
const getData = async () => {
try {
if (key) {
const jsonValue = await AsyncStorage.getItem(key);
setData(jsonValue != null ? JSON.parse(jsonValue) : '');
}
} catch (e) {
setError(e);
}
};
const mergeData = async () => {
try {
if (key && value && mergedValue) {
const jsonValue = JSON.stringify(value);
const mergedJsonValue = JSON.stringify(mergedValue);
await AsyncStorage.setItem(key, jsonValue);
await AsyncStorage.mergeItem(key, mergedJsonValue);
}
} catch (e) {
setError(e);
}
};
const removeData = async () => {
try {
if (key) {
await AsyncStorage.removeItem(key);
}
} catch (e) {
setError(e);
}
};
const getAllKeys = async () => {
let allKeys = [];
try {
allKeys = await AsyncStorage.getAllKeys();
setData(allKeys);
} catch (e) {
setError(e);
}
};
return {
data,
storeData,
getData,
removeData,
mergeData,
getAllKeys,
error,
};
};
export default AsyncStorageHooks;
this is my home component
const {data, error, getData, storeData, getAllKeys} =
useAsyncStorage('#word');
getData(); // this is works and use setData
storeData(); // this is works and use setData
getAllKeys();
console.log(data);
this works without any problems, they use the same page in the same state. It doesn't go into an infinite loop. The only getAllKeys dosen't works. Other functions works without any problems.
also side note: setData(allKeys); change to setData(allKeys + ''); or setData(JSON.stringify(allKeys)); stop to infinity loop why is that
You are getting an infinite loop because getAllKeys() is being called again and again. The first time it is called, there is this setData(keys) being called, which re-renders the component, because there is a state change.
When the component re-renders, getAllKeys() is called again, so setData(keys) is called, and it goes for ever. You would wanna use a useEffect to solve this problem, like so:
import {Text, View} from 'react-native';
import React, {useState, useEffect} from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
const Home = () => {
const [data, setData] = useState([]);
useEffect(()=>{
const getAllKeys = async () => {
let keys = [];
try {
keys = await AsyncStorage.getAllKeys();
setData(keys);
} catch (e) {
// read key error
}
};
getAllKeys();
},[])
console.log(data);
return (
<View>
<Text>hi</Text>
</View>
);
};
export default Home;

How do we check the boolean value in useEffect in react hooks?

I have received the boolean value and set to setNomStatus, but how can I check if that is true to show setShowCalender(true) ?
const [nomStatus, setNomStatus] = useState(false);
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus", {email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
if(nomStatus == true){
setShowCalender(true);
}
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
You can add another useEffect which watches this change, useEffect takes a second argument which is dependency array and the effect gets called if any of the dependency array value changes .
In this case since you need to make a decision based on the nomStatus, you can add it as a dependency to your useEffect
useEffect(() => {
if (nomStatus) {
setShowCalender(true);
}
}, [nomStatus]);
You can't since React state updates are asynchronously processed, the nomStatus state update won't be available until the next render cycle. Use the res.data[0].status value to set the showCalendar state.
const [nomStatus, setNomStatus] = useState(false);
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus",
{email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
if (res.data[0].status){
setShowCalender(true);
}
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
Or you can use a second useEffect hook with a dependency on nomStatus state update to set the showCalendar state.
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus",
{email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
useEffect(() => {
if (nomStatus){
setShowCalender(true);
}
}, [nomStatus]);

React Hook useEffect has a missing dependency: 'fetchUser'. useEffect problem?

I'm new to react and I'm learning how to use useEffect. I encountered this warning in my react app. I tried out some solutions on SO but the warning still remains. Both fetchUser and fetchPosts trigger this warning. Can anyone enlighten me what is the problem and what does the warning mean?
App.js
useEffect(() => {
setLoading(true)
const getUser = async () => {
const userFromServer = await fetchUser()
if (userFromServer) {
setUser(userFromServer)
setLoading(false)
} else {
console.log("error")
}
}
getUser()
}, [userId])
useEffect(() => {
const getPosts = async () => {
const postsFromServer = await fetchPosts()
setPosts(postsFromServer)
}
getPosts()
}, [userId])
useEffect(() => {
const getUserList = async () => {
const userListFromServer = await fetchUserList()
setUserList(userListFromServer)
}
getUserList()
}, [])
// Fetch user
const fetchUser = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
const data = await res.json()
return data
}
// Fetch posts
const fetchPosts = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
const data = await res.json()
return data
}
// Fetch list of users
const fetchUserList = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/')
const data = await res.json()
return data
}
If you are using any function or state which has been declared outside the useEffect then you need to pass it in the dependency array like this:
const someFunctionA = () => {
....
}
const someFunctionB = () => {
....
}
useEffect(() => {
....
}, [someFunctionA, someFunctionB])
You can read more about it here in case you want to know how it will be rendered: React useEffect - passing a function in the dependency array

Combine two Axios calls inside one useEffect with async/await

I'm currently trying to understand how to work with async/await in React JS. In this demo app, I'm trying to get full border names of the chosen country by calling https://restcountries.eu/. I make first API call to get info about country and the second one to get full name of its borders since first API call returns
only short border names. I believe there is a way to combine those calls inside one useEffect however everything I tried gave me some sort of an error or getting stuck in infinite loop. What is the proper way to combine those calls with async/await approach?
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [loading, setLoading] = useState(true);
const [country, setCountry] = useState({});
const [fullBorderNames, setFullBorderNames] = useState([]);
//FIRST API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
const fetchData = async () => {
setLoading(true);
try {
const response = await Axios(
`https://restcountries.eu/rest/v2/name/canada?fullText=true`,
{ cancelToken: source.token }
);
setCountry(response.data[0]);
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
};
fetchData();
return () => source.cancel();
}, []);
//SECOND API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
let borders = [];
if (country.borders) {
const fetchData = async () => {
try {
country.borders.forEach(async border => {
const response = await Axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`,
{ cancelToken: source.token }
);
borders.push(response.data[0].name);
if (borders.length === country.borders.length)
setFullBorderNames(borders);
});
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
setLoading(false);
};
fetchData();
}
return () => source.cancel();
}, [country.borders]);
if (loading) {
return <h2>Loading</h2>;
} else {
return (
<>
<pre>{JSON.stringify(country, null, 2)}</pre>
<pre>{JSON.stringify(fullBorderNames, null, 2)}</pre>
</>
);
}
};
export default App;
You can simply just make the requests right after the first one.
try {
const response = await Axios(`https://restcountries.eu/rest/v2/name/canada?
fullText=true`, { cancelToken: source.token });
const country = response.data[0];
setCountry(country);
/* all the other fetch calls*/
Can you tell me what kind of errors you get because I don't see an issue with doing them in the same useEffect? It just gets a little messy which can be refactored anyway.

Resources