I have a component which is updating states on page load with useeffect. But when i use history.goback() function to go back from a component to my bank component useeffect is not working. Here my code ;
bottomnavigator.js :
const goBack = () => {
if (props.history.location.pathname !== "/app/phone" && props.history.location.pathname !== "/") {
props.history.goBack();
}
};
bank.js :
const [transactions, settransactions] = useState([]);
const [bankBalance, setbankBalance] = useState(0);
const getBankData = async () => {
if (props.location.state) {
const res = await Nui.post(props.location.state, {});
if (res) {
setbankBalance(res);
}
}
}
const getTransactionData = async () => {
if (props.location.state) {
const res = await Nui.post('GET_BANK_TRANSACTIONS', {});
if (res) {
settransactions(res);
}
}
}
useEffect(() => {
getBankData();
getTransactionData();
}, []);
The issue is that you have listed "no dependencies" ([]) in your useEffect, so it will run once at the beginning and then never again. You should either omit the dependencies like so:
useEffect(() => {
getBankData();
getTransactionData();
});
Or list the correct set of dependencies like so: (I'm guessing here based on your code, but hopefully I got it right)
useEffect(() => {
getBankData();
getTransactionData();
}, [props.location.state]);
Related
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);
}
}
Gets list of emails from firestore and checks if current user is registered and then redirects them to sign up if they are new user.
The code is functional(it redirects succesfully) but get the following error:
arning: Cannot update a component (BrowserRouter) while rendering a different component You should call navigate() in a React.useEffect(), not when your component is first rendered.
const navigate = useNavigate();
let hasEmail = false;
const [emailList, setEmailList] = useState([]);
const emailRef = collection(db, "emails");
useEffect(() => {
const getEmails = async () => {
const data = await getDocs(emailRef);
setEmailList(
data.docs.map((doc) => ({
...doc.data(),
}))
);
};
getEmails();
}, []);
const emailCheck = (emails) => { //checks if email exists
hasEmail = emails.some((e) => e.email === auth.currentUser.email);
};
const direct = () => { // redirects to required page
if (hasEmail) {
navigate("/index");
} else {
navigate("/enterdetails");
}
};
emailCheck(emailList);
direct();
Move the email checking logic into a useEffect hook with a dependency on the emailList state.
const navigate = useNavigate();
const [emailList, setEmailList] = useState([]);
const emailRef = collection(db, "emails");
useEffect(() => {
const getEmails = async () => {
const data = await getDocs(emailRef);
setEmailList(
data.docs.map((doc) => ({
...doc.data(),
}))
);
};
getEmails();
}, []);
useEffect(() => {
if (emailList.length) {
const hasEmail = emailList.some((e) => e.email === auth.currentUser.email);
navigate(hasEmail ? "/index" : "/enterdetails");
}
}, [auth, emailList, navigate]);
This might not run without the proper firebase config but check it out
https://codesandbox.io/s/elated-bell-kopbmp?file=/src/App.js
Things to note:
Use useMemo for hasEmail instead of emailCheck. This will re-run only when emailList changes
const hasEmail = useMemo(() => {
//checks if email exists
return emailList.some((e) => e.email === auth.currentUser.email);
}, [emailList]);
There isn't really a point in having this in a react component if you are just redirecting away. Consider having the content of 'index' at the return (</>) part of this component. Only redirect if they aren't authorized
useEffect(() => {
if (!hasEmail) {
navigate("/enterdetails");
}
//else {
// navigate("/index");
//}
}, [hasEmail, navigate]);
i have loading screen for call all the data function.i used async function for all function call.
//NOTE: this screen loads all the data and store it in store so user will have a smother experience
const LoadingScreen = (props) => {
const gotToHomeScreen = () => {
props.navigation.replace("Home", { screen: HOME_SCREEN });
};
//NOTE: loading data here for home screen journey
const getRequiredAPIDataInStore = async () => {
GetAllFieldProp();
GetAllSalaryAPIResponse();
GetSalaryAPIResponse();
let { spinnerStateForm101 } = GetForm101API();
let { spinnerStateForm106 } = GetForm106API();
GetMessagesCountAPI();
GetMessagesAPI(props);
GetAllFormAPIResponse();
GetAllSpecificSalaryAPIResponse();
let { spinnerStateMonthly } = GetMonthlyAbsenceAPI(props);
let { spinnerStateWeekly } = GetWeeklyAbsenceAPI(props);
if (
spinnerStateMonthly &&
spinnerStateWeekly &&
spinnerStateForm106 &&
spinnerStateForm101
) {
gotToHomeScreen();
}
};
getRequiredAPIDataInStore();
export default LoadingScreen;
but i am getting warning messages for this.
Warning: Cannot update a component from inside the function body of a different component.
at src/screens/loading-screen.js:19:26 in gotToHomeScreen
at src/screens/loading-screen.js:37:6 in getRequiredAPIDataInStore
How to solve this warning messsage?
Here's the approach I would take.
const Loading = () => {
const [spinnerStateMonthly, setSpinnerStatMonthly] = useState(null);
const [spinnerStateWeekly, setspinnerStateWeekly] = useState(null);
const [spinnerStateForm106, setspinnerStateForm106] = useState(null);
const [spinnerStateForm101, setSpinnerStateForm101] = useState(null);
const gotToHomeScreen = () => {
props.navigation.replace("Home", { screen: HOME_SCREEN });
};
useEffect(() => {
// async callback to get all the data and set state
(async () => {
await GetAllFieldProp();
await GetAllSalaryAPIResponse();
await GetSalaryAPIResponse();
const { spinnerStateForm101: local101 } = GetForm101API();
const { spinnerStateForm106: local106 } = GetForm106API();
setSpinnerStateForm101(local101);
setSpinnerStateForm106(local106);
await GetMessagesCountAPI();
await GetMessagesAPI(props);
await GetAllFormAPIResponse();
await GetAllSpecificSalaryAPIResponse();
const { spinnerStateMonthly: localMonthly } = GetMonthlyAbsenceAPI(props);
const { spinnerStateWeekly: localWeekly } = GetWeeklyAbsenceAPI(props);
setSpinnerStateMonthly(localMonthly);
setSpinnerStateWeekly(localWeekly);
})();
}, []);
// effect to check for what the state is and if all the states are satisfied
// then go to the home screen
useEffect(() => {
if (spinnerStateMonthly
&& spinnerStateWeekly
&& spinnerStateForm106
&& spinnerStateForm101) {
gotToHomeScreen();
}
}, [spinnerStateMonthly, spinnerStateWeekly, spinnerStateForm101,
spinnerStateForm106]);
};
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
GET requests canceling fine in this example:
export default function Post (props) {
const _cancelToken = axios.CancelToken.source()
useEffect(() => {
const _loadAsyncData = async () => {
await axios.get('/post'), { cancelToken: _cancelToken.token })
}
_loadAsyncData()
return () => {
_cancelToken.cancel()
}
}, [])
return ()
}
But when I need save form via POST request, my code looks like:
export default function Form (props) {
const _cancelToken = axios.CancelToken.source()
const _zz = { qq: 'QQ' }
const handleCreate = async e => {
e.preventDefault()
_zz.qq = 'ZZ'
await axios.post('/form'), {}, { cancelToken: _cancelToken.token })
}
useEffect(() => {
return () => {
console.log(_zz.qq)
_cancelToken.cancel()
}
}, [])
return ()
}
Request not cancel and my _zz.qq always 'QQ' instead 'ZZ'. It's working fine without hooks, but I like hooks and want to use hooks for new components.
I want to cancel request when componentWillUnmount.
This is because you're losing the changes between renders. During the handleCreate call the variable changes only for that render. When the useEffect is run on a subsequent render/unmounting, you're resetting _zz to { qq: 'QQ' }. In order to get around this you need to use references.
export default function Form (props) {
const cancelToken = useRef(null)
const zz = useRef({ qq: 'QQ' })
const handleCreate = async e => {
e.preventDefault()
cancelToken.current = axios.CancelToken.source()
zz.current = { qq: 'ZZ' }
await axios.post('/form'), {}, { cancelToken: cancelToken.current.token })
}
useEffect(() => {
return () => {
console.log(zz.current) //this should now be {qq : 'ZZ'}
if (cancelToken.current) {
cancelToken.current.cancel()
}
}
}, [])
return null
}