UseEffect, Re-run on state change? - reactjs

useEffect(() => {
if (params.usersearch === props.search || "random" === props.search) {
console.log('same');
return;
}else{
console.log(props.search);
console.log('Params is :',params.usersearch);
let api = `https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${params.usersearch}&per_page=24&format=json&nojsoncallback=1`;
props.handleLoading(true); //changing state
let fetchedData = axios
.get(api)
.then((data) => data.data.photos.photo)
.catch((err) => console.log(err));
props.handleData(fetchedData); //changing state
props.handleSearch(params.usersearch); //changing state
props.handleLoading(false); //changing state
const title = document.querySelector("title");
title.textContent = `Flickr-Photos/${params.usersearch}`;
}
},[params.usersearch]);
Hi everyone. I have a question , If my useEffect is running and it in between changes state(as I have mentioned when the props.handleLoading function gets triggered ), so is it gonna stop the and re-run the useEffect method or it is gonna complete the code?

This kind of code execution cannot be implicit stopped.
Answer:
The useEffect-callback will be called again, even if the previous is not done
You could use a debounce or blocking behavior and cancel/ignore the previous action.
UseEffect supports a clean-up method. You could return a function to cancel a timer with a given throttle value (for debounce).
As stated by the react docs
Why did we return a function from our effect? This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They’re part of the same effect!
When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time. We’ll discuss why this helps avoid bugs and how to opt out of this behavior in case it creates performance issues later below.
Solution
Stackblitz Example

Related

socket.io fires event twice in react useEffect hook even though i have unsubscribed in the cleanup function [duplicate]

This question already has answers here:
Why useEffect running twice and how to handle it well in React?
(2 answers)
Closed last month.
socket.io in react useEffect renders twice even though i unsubscribed in the cleanup function
the code is something like this
useEffect(() => {
const callback = (date) => {
//do something with data
console.log("new chat received");
};
socket.on("new-chat-message", callback);
return () => {
socket.off("new-chat-message", callback);
};
}, []);
the only way this useEffect hook render once is when i remove the strict mode from the App.tsx
similar problems suggests i should remove the event listener on cleanup but that doesn't work for me
"the only way this useEffect hook render once is when i remove the strict mode from the App.tsx"
If this is the case, then there's no issue at all.
Strict Mode helps for debugging purposes. Production builds will ignore React.StrictMode - it just gets taken out. Strict mode leads to your components rendering twice if it's not a prod build, which is why you're seeing the log twice.
So, you're all good.

Can anyone give more info about the useEffect hook based on experience

I understand a bit about the useEffect hook but I think there’s still more knowledge to grasp. Some of which are not in the documentation. Please any contribution will help a lot y’all.
Some of my questions are:
Does useEffect get called on initial render even in production just like in development?
If it does, how do we control this the best way?
How can we use a clean up function on this Hook?
How can we make asynchronous calls in useEffect?
My attempts on useEffect usually makes me feel like a bad developer
Please take a look at react docs and react beta docs.
It always runs when your component mounts, after the first render regardless of the environment. In development mode when strict mode is on, it runs twice:
When Strict Mode is on, React will run one extra development-only setup+cleanup cycle before the first real setup. This is a stress-test that ensures that your cleanup logic “mirrors” your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, you need to implement the cleanup function.
I'm not really sure what you mean by controlling it the best way. Your effect or setup code runs whenever the component mounts. Maybe
How to handle the Effect firing twice in development? can help you. You sometimes might want to prevent the effect to be executed when the component mounts, you can skip the effect by using a ref. See this stackoverflow question
The function you return in the useEffect does the clean up for you. See. For instance if you add an event listener inside useEffect, you remove the listener inside the function you return inside of it. See this link
useEffect(() => {
const listener = () => { /* Do something */ };
window.addEventListener("click", listener);
return () => {
window.removeEventListener("click", listener);
};
}, []);
Yes you can. See this stackoverflow question and fetching data in docs
useEffect(() => {
async function asyncFunction() {
/* Do something */
}
asyncFunction();
}, []);
Update:
Take a look at You Might Not Need an Effect
. It explains some situations which you might not need an effect at all.
Removing unnecessary Effects will make your code easier to follow, faster to run, and less error-prone.
Update 2:
You can probably skip this part for now, but it might help you to have a better grasp of useEffect, event handlers and what to expect in the future.
Separating Events from Effects tries to explain the differences between effects and event handlers, why distinguishing between those two is important and using event handlers inside effects.
Event handlers only re-run when you perform the same interaction again. Unlike event handlers, Effects re-synchronize if some value they read, like a prop or a state variable, is different from what it was during the last render. Sometimes, you also want a mix of both behaviors: an Effect that re-runs in response to some values but not others. This page will teach you how to do that.
Sometimes, you might use an event handler which has access to the props or the state inside an effect. But you don't want the useEffect to be triggered every time the values used in the event handler change. The following example is taken form useEffect shouldn’t re-fire when event handlers change
.
function Chat({ selectedRoom }) {
const [muted, setMuted] = useState(false);
const theme = useContext(ThemeContext);
useEffect(() => {
const socket = createSocket('/chat/' + selectedRoom);
socket.on('connected', async () => {
await checkConnection(selectedRoom);
showToast(theme, 'Connected to ' + selectedRoom);
});
socket.on('message', (message) => {
showToast(theme, 'New message: ' + message);
if (!muted) {
playSound();
}
});
socket.connect();
return () => socket.dispose();
}, [selectedRoom, theme, muted]); // 🟡 Re-runs when any of them change
// ...
}
As you see, you do not want to reconnect every time theme or muted variables change. The only time you want the effect(connecting and disconnecting from the server) to run is when the selectedRoom value changes.
So the react team has proposed a RFC: useEvent which provides
A Hook to define an event handler with an always-stable function identity.
useEvent is an experimental and unstable API that has not yet been added to the React(stable versions) ye, so you can’t use it yet.
This might be off-topic but probably helps you to understand React and its lifecycles better: There is this issue useCallback() invalidates too often in practice issue on GitHub . One workaround would be to create a custom hook that returns a function that its identity is stable and won't change on re-renders:
function useEventCallback(fn) {
let ref = useRef();
useLayoutEffect(() => {
ref.current = fn;
});
return useCallback(() => (0, ref.current)(), []);
}
Or you could use the use-event-callback package.
Note that useEventCallback does not mimic useEvent precisely:
A high-fidelty polyfill for useEvent is not possible because there is no lifecycle or Hook in React that we can use to switch .current at the right timing. Although use-event-callback is “close enough” for many cases, it doesn't throw during rendering, and the timing isn’t quite right. We don’t recommend to broadly adopt this pattern until there is a version of React that includes a built-in useEvent implementation.
useEffect is a very powerful hook. Regarding your question:
useEffect(() => (), []) - this version without params will be called once on initial rendering
you can control useEffect with params [] and based on these params you can place some logic inside the callback function.
clean up function used before unmount of your component, it is a good place to remove listeners or close connection to resources like Databases, Camera and etc.
Example of async call
useEffect(() => {
// declare the data fetching function
const fetchData = async () => {
const data = await fetch('https://yourapi.com');
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);
}, [])

useEffect() in my search Bar component causes an infinite loop, that I cannot understand

I'm new to React-Native and am following along in a course on react-native.
This git hub links to a my repository the code has the problem(infinite loop) I describe in the following question.
I have spent 12+ hours trying to figure this out. Please help me figure this out if you can.
https://github.com/JohnyClash/mealsToGo
useEffect in question
above photo directory: 'src/features/restaurants/components/search.components.js'
useEffect(() => {
search(searchKeyword);
}, []);
The above code creates a feedback loop that causes the app to continuously fetch from a mock api, that returns the location information, loads to the screen and then quickly reloads ad infinitum.Its intended purpose is to run a single time on component mount to cause a single default search. Instead, This useEffect() inside of search.component runs its callback repeatedly. The useEffect is not tracking a dependency that has changed, and is given [] an empty array in place of dependency
useEffect(callback,dependencies)
useEffect(callback,[])
Shouldn't this syntax of useEffect only run once after its mount, and not run again if something is updated? If my understanding is correct how is it possible that this use effect is running in an infinite loop?
this useEffect() is the culprit as the infinite reload loop stops when it is this useEffect() is removed.
all other functionality down this logic chain does not create an infinite loop, as in the search method initiated through onSubmitEditing works well without looping.
The problem of this infinite loop is being caused by this location object here, probably because every time the LocationContext is rerendered (a search is done or state is updated), it creates a new instance of the location object, which makes the useEffect be called again, then it search again, recreates the location object when calling useEffect again, which makes a new search, update some state and recreates the location object...
Code with problem of infinite loop:
useEffect(() => {
console.log(location)
if (location) {
const locationString = `${location.lat},${location.lng}`;
retrieveRestaurants(locationString);
}
}, [location]);
If you do something like this, might solve this problem:
useEffect(() => {
if (location?.lat && location?.lng) {
const locationString = `${location.lat},${location.lng}`;
retrieveRestaurants(locationString);
}
}, [location?.lat, location?.lng]);
Also be careful with setTimeout, and not clearing it on component unmount
Tip: Always avoid object, array or function in useEffect dependency, and if necessary it needs to be memorized with useMemo or useCallback
Try This :
useEffect(() => {
search(keyword)
}, [keyword])

InfiniteLoop in useEffect when one of the dependency is a function from useContext

2021 UPDATE
Use a library that makes requests and cache them - react-query, swr, redux-toolkit-query
ORIGINAL QUESTION
I've been struggling with this for quite a long time and didn't find an answer.
I have a component that is the last step of some registration process during which I ask a user to enter its data through several forms. In this component, I send collected data to API. If the request is successful I show ok, if not I show error.
I have useEffect that sends the data. The function that performs this task lives in a context
const { sendDataToServer } = useContext(context)
useEffect(() => {
const sendData = async () => {
setLoading(true)
await sendDataToServer(...data)
setLoading(false)
}
sendData()
}, [sendDataToServer, data])
If I include sendDataToServer in the dependencies list this useEffect would go into an infinite loop, causing endless rerendering. I suppose this is because a reference to the function has a different value on every render.
I can of course redesign the app and do not keep the function in the context, but I do like it and don't consider it a bad practice (correct me if I am wrong)
So what are my options here? How do I keep the flow with the context API, but use useEffect with the correct list of dependencies?
You're right with your guess, that's why we got useCallback for referential equality.
You didn't post the sendDataToServer function, but it should look something like this with useCallback:
const sendDataToServer = useCallback(data => {
// your implementation
}, [your, dependencies])
After that you can safely use it in your useEffect.
I highly recommend Kent C. Dodd's blog posts: When to useMemo and useCallback
Smartassing now: If it's only purpose is sending data to the server (and not changing the app's state), I don't know why it should be part of the context. It could be a custom hook or even a static function.
Btw: There could be another problem: If the data dependency in your useEffect is changed when executing sendDataToServer, you will still have an endless loop (e. g. when you fetch the new data after executing sendDataToServer), but we can't see the rest of the code.

Mobx: change states in action without runInAction still works

In mobx documentation:
action only affects the currently running function, not functions that are scheduled (but not invoked) by the current function! This means that if you have a setTimeout, promise.then or async construction, and in that callback some more state is changed, those callbacks should be wrapped in action as well!
This above mean, I should wrap state changed with runInAction, like this following:
class App {
#observable logined = false
#action async login(payload){
runInAction(() => {
setTimeout(() => {
this.logined = false
}, 1000)
})
}
}
Above works, but the weird is if i remove the runInAction block, code still works, this behaviour is inconsistent with the document said.
please check the fiddle.
This behavior is correct; unobserved data can be modified at will, as it cannot lead to further side effects, see: https://github.com/mobxjs/mobx/blob/master/CHANGELOG.md#310
Also not that it is always allowed to change state outside of actions as long as strict mode is not enabled (mobx.useStrict(true))
A PR to reflect this new behavior better in the docs would be appreciated! https://github.com/mobxjs/mobx/blob/gh-pages/docs/refguide/action.md
An "action" allows you to make multiple state changes in one "batch" -- if you are only making one change, you don't need it.

Resources