why this code give me infinite loop?
import { useState, useEffect } from 'react';
import './style.css';
import { faker } from '#faker-js/faker';
export default function App() {
const data = faker.name.firstName();
const [name, setName] = useState(data);
useEffect(() => {
setName(faker.name.firstName());
}, [name]);
return <div>{name}</div>;
}
https://stackblitz.com/edit/react-ts-6ept5o?file=App.tsx
I want a new generated name using faker everytime i refreshed the page.
Well there's several problems here. Firstly, React state isn't persisted when you refresh the page. To do that you'd have to web storage or some library to help store when changing/refreshing the page. So you don't need an effect here nor to worry about getting new fake data on refresh, you will.
Also, when a functional component re-renders, the entire function gets run again. So this: const data = faker.name.firstName(); gets run every time you render. If you want to use it as an initial state, use the function argument to useState which runs it once on page load: useState(() => faker.name.firstName());
Finally, in regards to the infinite loop, your effect has name as a dependency, but inside it you also set name state. So you're getting a loop.
Component renders:
-> effect runs, setting name state
-> set state triggers re-render with new name
-> new name triggers effect, since name is a dependency
-> effects sets name state to new value
-> set state triggers re-render
and so on. But in this case, you don't actually need an effect at all, given reasons mentioned above
You have a useEffect that runs every time name changes.
When it runs, you're setting name to a new value.
This changes name and triggers the useEffect again.
When it runs, you're setting name to a new value.
This changes name and triggers the useEffect again.
And so on...
You need to understand how useEffect works.
you have set name in useEffect array dependency. So everytime the component renders name changes, as name changes your component renders again and goes into infinity loop. So you leave empty array:
const [name, setName] = useState("");
useEffect(() => {
setName(faker.name.firstName());
},[]);
return <div>{name}</div>
Related
I have a parent component that sends a useState string to its child component.
I can access that useState but I want to set it in another one but I am not able to do that because it rerenders all the time and it breaks the app.
Child component:
const Child = ({firstValue}) => {
const [secondValue,setSecondValue) = useState(firstValue);
}
First, it takes the default value but it never updates it.
I want to update it automatically when the firstvalue changes its value.
I have tried to set it by setSecondValue(firstValue) but it runs in a infinite rerender loop and it breaks.
I also tried with: const [secondValue,setSecondValue) = useState(...secondValue, {value: firstValue});
Also it is worth to mention that I need it to be updated onload, not in any onClick or onChange function.
need to use useEffect to update secondValue when firstValue changes
useEffect(() => {setSecondValue(firstValue)}, [firstValue])
What's the different between useEffect when you pass it dependencies as the second parameter and useCallback?
Don't both essentially run the function/code passed as the first parameter whenever the dependencies passed as the second parameter change?
From what I've read the two hooks are intended to serve different purposes, but my question is whether they in actuality could be used interchangeably because they functionally do the same thing
They're too different.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
You can't switch useEffect with useCallback alone because you also need the logic to run the newly created function. (I suppose you could implement this if you used a ref as well, but that'd be quite strange.)
You can't switch useCallback with useEffect because you very often don't want to run the newly created function immediately - rather, you usually want to pass it as a prop to some other component.
useCallback primarily exists for optimization purposes, to reduce re-renders of a child component.
No, They are not same.
useEffect - is used to run side effects in the component when something changes. useEffect does
not return you anything. It just runs a piece of code in the component.
useCallback - Whereas useCallback returns a function, it does not execute the code actually. It is important to understand that
functions are objects in Javascript. If you don't use useCallback, the function you define inside the component is
re-created whenever the component rebuilds.
Example
Consider this example, this component will go in a infinite loop. Think Why?
const TestComponent = props => {
const testFunction = () => {
// does something.
};
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
Because on each render the testFunction
would be re-created and we already know that ueEffect will run the code when ever the testFunction changes. And since testFunction changes on each render, the useEffect will keep on running, and hence an infinite loop.
To fix this, we have to tell react, hey please don't re-create the testFunction on each render, create it only on first render (or when something changes on which it depends).
const TestComponent = props => {
const testFunction = useCallback(() => {
// does something.
}, []);
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
This won't be a infinite loop, since instance of testFunction will change only on first render and hence useEffect will run only once.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
Let's take an example, If I run the below code and click the first button it'll always rerender MemoComponent as well. Why because every time
we are passing new onClick function to this. To avoid re-rendering of MemoComponent what we can do is wrap onClick to useCallback. Whenever you want to create a new function pass state to the dependence array.
If you want to perform some action on state change you can write inside useEffect.
const Button = ({ onClick }) => {
console.log("Render");
return <button onClick={onClick}>Click</button>;
};
const MemoComponent = React.memo(Button);
export default function Home() {
const [state, setState] = useState(1);
useEffect(() => {
console.log(state); // this will execute when state changes
}, [state]);
const onClick = () => {};
// const onClick = useCallback(() => {},[])
return (
<main>
<button onClick={() => setState(1 + state)}>{state}</button>
<MemoComponent onClick={onClick} />
</main>
);
}
useEffect
It's the alternative for the class component lifecycle methods componentDidMount, componentWillUnmount, componentDidUpdate, etc. You can also use it to create a side effect when dependencies change, i.e. "If some variable changes, do this".
Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, [state]);
OR
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, []);
useCallback
On every render, everything that's inside a functional component will run again. If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change. You should use it when a function is a dependency of a side effect e.g. useEffect.
Whenever you have a function that is depending on certain states. This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.
const myFunction = useCallback(() => {
// execute your logic for myFunction
}, [state]);
Without useCallback, myFunction will be reassigned on every render. Therefore it uses more compute time as it would with useCallback.
const { category } = useParams();
const [sort, setSort] = useState(category);
This think is really driving me crazy - when i console the category, it is actually changing, but sort state doesn't..
You need to do some reading in order to understand how React Hooks work. There are many tutorials online - the React API documentation is even a good start:
What do we pass to useState as an argument? The only argument to the useState() Hook is the initial state.
From the React docs. Notice they say the argument is the initial state only.
When you do useState(X) - the value X is only used the first time the component is rendered. On subsequent renders, if and when X changes, and you want to use the new X, you must update the state value yourself via the set function returned by useState.
In your case, we can say that the state variable changes whenever category changes, so category is a dependency of the state variable. When you have a dependency of this kind, you know you need to use an effect, by way of the useEffect hook:
const { category } = useParams();
const [sort, setSort] = useState(category);
// This callback will run whenever the dependencies
// specified in the second argument change.
useEffect(() => {
setSort(category)
}, [category]) // <= the dependencies
You are expecting whenever category changes, sort should also changes, if this is the goal, try this:
useEffect(() => setSort(category), [category]);
if you only want to change sort first time on initial render, try this:
useEffect(() => setSort(category), []);
For more info, see Using the Effect Hook - React
I am trying to setup a functional component that allows the user to copy/paste their clipboard image into the app. In useEffect, I setup document.onpaste . This was working and I was able to get the image and upload it. But I need to have it update the state after paste, so that I can allow them to paste multiple images and keep them in array in state.
I've simplified the problem below. Basically when I paste I want to have it update the counter value, but it doesn't. Counter always = 0. If I use normal react button onclick event, it works fine. I'm guessing the onpaste event is outside of React, but then,
How can I process the paste event and also update the functional state?
import React, { useEffect, useState } from 'react';
const AddAttachmentsContainer = ({ zoneid, tableid, field }) => {
const [counter,setCounter]=useState(0);
useEffect(() => {
document.onpaste=(e)=>{GetImage(e);e.preventDefault()}
},[])
const GetImage = (e) => {
setCounter(counter+1)
console.log(counter)
}
}
If I am not mistaken, I'll bet that the state is updated. useState will update the state and trigger a re-render. Since it is asynchronous, console.log will be called with the old state value.
useEffect(() => console.log(counter), [counter]);
... don't forget to add counter in the second argument of useEffect. see here...
Also see here
Alternatively, of course, you can also use refs, but be aware of the use cases for them. It is easy to allow refs to unnecessarily spread through your code.
To be honest, it might be wise to just setCounter in the useEffect you already have and call your GetImage function afterwards... from there, you can retrieve counter. There are multiple approaches to this.
I want to create a component Person that is fully controlled by its state. It should also be able to sync the props change (firstName, lastName) passed from its parent component to the state. I have the following code. It does what I want which is syncing props to state and re-render after state has been changed.
However one issue I noticed is that useEffect gets invoked after DOM update triggered by the parent props change. So the initial re-render is wasted since I only want it to re-render after useEffect gets invoked and state is changed.
import React,{useState, useEffect} from 'react';
const Person = ({firstName, lastName}) => {
const [name, setName] = useState(firstName + lastName)
useEffect(() => {
setName(firstName + lastName);
console.log("state changed!");
}, [firstName, lastName])
console.log("re-render!");
return <div>render {name}</div>;
}
export default Person;
I created a simple demo here https://codesandbox.io/s/sparkling-feather-t8n7m. If you click the re-render button, in the console you will see below output. The first re-render! is triggered by props change which I want to avoid. Any idea how to achieve this? I know there are other solutions such as making it fully uncontrolled, but I'd like to know if there is any workaround to this solution
re-render!
state changed!
re-render!
you will need to add a condition in your useEffect. something like :
const [didUpdate, setDidUpdate] = useState(false);
useEffect(() => {
if(didUpdate){
setName(firstName + lastName);
console.log('state changed!');
} else {
setDidUpdate(true);
}
}, [firstName, lastName]);
Here it reproduce the componentDidUpdate() behavior.
On the first rendering, component is mounted, didUpdate is initialised to false, so the effect will only set it to true for the next updates.
Note that a state (useState) initialised with a props isn't updated when the prop changes.
It's basically React core behavior, according to doc
The function passed to useEffect will run after the render is committed to the screen.
in order to avoid bugs caused by side effects.
I guess you simplified your example a bit, but you should know that copying props to state unconditionally is an anti-pattern https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#anti-pattern-unconditionally-copying-props-to-state
Instead, you should directly use props :
const Person = ({firstName, lastName}) => (
<div>render {firstName + lastName}</div>;
);