React useEffect prevent on initial mount - reactjs

i have five a snapshot listener in useEffect and i have another call api to get data from firestore and update state
but I am facing a problem is every initial mount all listener got called , my goal is i want to all listener called only when document changed
i tried with useRef it works but listener do not trigger
As you can see in the example below, onSnapshot is printed during the initial mounted
useEffect(() => {
if (isFirstMount.current) return;
someFirestoreAPICall.onSnapshot((snap) => {
//called every initial mount
});
someFirestoreAPICall.onSnapshot((snap) => {
//called every initial mount
});
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
}, []);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
let snap = await someFirestoreAPICall.get();
setData(snap.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
setLoading(false);
};
fetchData();
}, []);
Codesandbox

You can use a condition inside your useEffect block as you are doing, I think. But perhaps useState would be more appropriate here than useRef.
eg:
const [state, setState] = useState(null)
useEffect(()=>{
if (state) {
// do something
}
}, [state])
The useEffect will run on mount and every time you change the value of state, but code inside the condition will only run if you change the state to a truthy value.

Related

Stop setInterval function written inside a useEffect when navigated to another page

I have an API that needs to be fetched every 30 seconds.The UseEffect written below in the code is for a component that gets rendered on home component. It's working well but if I navigate to any another page I need the API to not be fetched.
I'm using react-router and redux.
useEffect(() => {
dispatch(loadCurrencyList())
setInterval(() => {
dispatch(loadCurrencyList())
}, 30000
)
}, [dispatch])
Do a cleanup in the return of useEffect :
useEffect(()=>{
const timer = setInterval(...)
return ()=> clearInterval(timer)
}, [dispatch])
Store timer id in a mutable variable created with useRef() hook,
then use it in the component unmount code. Don't return clean up function from useEffect() dependent on [dispatch] as it will be executed every time dispatch variable changes.
const timer = useRef();
useEffect(() => {
dispatch(loadCurrencyList());
timer.current = setInterval(() => {
dispatch(loadCurrencyList())
}, 30000);
}, [dispatch]);
useEffect( () => () => {
if (timer.current) {
clearInterval(timer.current);
}
}, []);

React- Issue with infinite loop in useEffect

I have a useEffect() that fetches the data through axios, I want that to render only one time so I passed an array. Everything works fine, but the problem is whenever I try to sort the items, the second useEffect fires just followed by the first useEffect, which is causing the component to fetch the items all over again and again.
const [products, setProducts] = useState([]);
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, [products]);
This is because you passed an array containing your products state, rather than an empty array, which will fire useEffect on state change (for products state specifically). Try changing your code to an empty array:
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, []);
As #skyboyer mentioned below, it is good to note that state is not updated in a synchronous manner. Therefor, console.log(products) will not reflect an accurate value for your state when useEffect runs.
It is okay to use multiple useEffect hooks. If you would like to view your updated state in the console, or do some other work with it, you could add another useEffect hook and pass your state into the array:
useEffect(() => {
console.log(products);
}, [products]);
Since products is in the useEffect dependency array, it is going to run every time there are changes made to the products state. getProducts() runs setProducts which then in turn is going to trigger the use effect again. Using an empty array in the useEffect will tell it to only run when the component is mounted.
Like this:
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, []);

React useEffect to run on both component mounting and dependency change

I want to know how to run the useEffect side effect in both component mounting and a dependent value change. Currently I'm using two useEffects to achieve this like this.
useEffect(() => {
let isMounted = true;
const getUsers = async () => {
try {
const userResponse = await api.get('/users');
if (isMounted) { setUsers(userResponse.data); }
} catch (error) {
console.log(error);
}
};
getUsers();
}, []);
useEffect(() => {
let isMounted = true;
const getUsers = async () => {
try {
const userResponse = await api.get('/users');
if (isMounted) { setUsers(userResponse.data); }
} catch (error) {
console.log(error);
}
};
getUsers();
}, [netInfo]);
Is there anyway to achieve this using one useEffect?
Runs when the component is mounted for the first time and on every re-render
useEffect(() => {})
Runs when the component is mounted for the first time alone
useEffect(() => {}, [])
Runs when the component is mounted for the first time and whenever the someDependency's value changes .
useEffect(() => {}, [someDependency])
You can remove the first useEffect .

Handling multiple useEffect hooks

I am calling useEffect hook multiple times in a component
useEffect(() => {
getData(id);
}, [getData, id]);
const reqBody = useMemo(
() => ({
item: data?.item,
}), [data?.item]
);
useEffect(() => {
if (data?.item) {
getAnotherData(reqBody);
}
}, [getAnotherData]);
As seen in the code snippet, the first useEffect hook calls a function called getData which stores the data in redux store and the reqBody of the function getAnotherData in the second hook depends on the first hook.
Therefore, the second hook runs even if the data in the store is not updated yet resulting in multiple api calls.
How can I avoid this so that the function getAnotherData is called only when the data in the store is updated?
Use data in the dependency array instead of getAnotherData in the second useEffect hook.
useEffect(() => {
getData(id);
}, [getData, id]);
const reqBody = useMemo(
() => ({
item: data?.item,
}), [data?.item]
);
useEffect(() => {
if (data?.item) {
getAnotherData(reqBody);
}
}, [data]);

React updating a setState with a callback to a useState hook

I am updating the setStates I am using from class based components to functional ones. I have a few usecases where theres a closure. the code is
this.setState(
({ notifications }) => ({
notifications: [...notifications, notification],
}),
() =>
timeout !== undefined && window.setTimeout(closeNotification, timeout)
);
How do I use the useState hook to update this. I assume useState doesnt return a promise, so I cant use a .then. If thats the case, whats the best way to update this function?
Maybe something like so?
const [notifications, setNotifications] = useState([]);
const doSomething = () => {
setNotifications([ /* ... */ ]);
}
useEffect(() => {
let timer;
if(timeout !== undefined) {
timer = setTimeout(closeNotification, timeout);
}
return () => clearTimeout(timer)
}, [notifications]);
You can use the useState and the useEffect hooks to achieve the same result like so:
const [notifications, setNotifications] = useState([]);
useEffect(() => {
// Close Notifications Here
}, [notifications]);
// To update the state use the setNotifications function like so
setNotifications(prevState => [...prevState, newNotification]);
the useEffect hook will run whenever you update the notifications.

Resources