I am having to set a const value using useState hook in FormDataConsumer. This is necessary because the value of the const can only be gotten within FormDataConsumer. This const value will be used as a prop to another component. The problem I am having is the error message below. What is the best way to do this? Thanks in advance
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
//const initialized here
const [outletsList, setOutletsList] = useState([]);
//the place where the const must be set
<FormDataConsumer>
{({ formData, ...rest }) => {
//console.log(`formData: ${JSON.stringify(formData)}`)
//console.log(`rest: ${JSON.stringify(rest)}`)
let s = GetOutletsBySalesRep({
distributorkey: formData.distributorid,
salesrep: formData.salesrep,
});
**setOutletsList(s);**
}}
</FormDataConsumer>
The error you get is because of the way React itself works - you update a state variable which makes the component to rerender and on each render you set the state again ... so you end up in an endless loop.
To escape it you should either set a condition upon which the state will be updated e.g.
if(outletsList.length === 0) setOutletsList(s);
Or use a reference to save the result through the useRef hook updating it's current property because this operation doesn't trigger a rerender.
const outletsListRef = useRef();
...
outletsListRef.current = s;
Although not sure why you would need to save it and not rerender the component.
Related
So, React has UseState hook, and we should use it like this.
const [value, setValue] = useState(0);
And if we want to change a value, it should be written like this:
const increase = () => {
setValue(value + 1)
}
Why it's not possible to write it like this? Why we need setValue?
const increase = () => {
return value + 1;
}
I understand that it just doesn't work, but I couldn't find an explanation why it is like that.
The useState hook returns a function to update the state. Using setValue(value) method, you can get the previous(or old) state value to update the state.
On the other hand, without useState when you tries to increment the value, the value will not change because the react rendered the component only once and since there is no state change it won't get re-rendered, the value will remain at 0.
You can see here in details : https://dev.to/aasthapandey/why-to-use-usestate-in-react-pkf
Answer is simple...! The state variable is directly linked to rendering your page. The state variable of useState hook is read only for the component to render it in the below return statement which renders the JSX.
The setValue() function not only changes the state value, but will also trigger the react to re-render the DOM which has already being painted on the browser.
Well that's a difference between an ordinary HTML page & a React App. React provides amazing framework to re-render the page automatically after the you alter the state using the setValue() fucntion
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])
I am currently trying to learn about the inner workings of React in context of when a component is re-rendered or especially when (callback-)functions are recreated.
In doing so, I have come across a phenomenon which I just cannot get my head around. It (only) happens when having a state comprising an array. Here is a minimal code that shows the "problem":
import { useEffect, useState } from "react";
export function Child({ value, onChange }) {
const [internalValue, setInternalValue] = useState(value);
// ... stuff interacting with internalValue
useEffect(() => {
onChange(internalValue);
}, [onChange, internalValue]);
return <div>{value}</div>;
}
export default function App() {
const [state, setState] = useState([9.0]);
return <Child value={state[0]} onChange={(v) => setState([v])} />;
}
The example comprises a Parent (App) Component with a state, being an array of a single number, which is given to the Child component. The Child may do some inner workings and set the internal state with setInternalValue, which in turn will trigger the effect. This effect will raise the onChange function, updating a value of the state array of the parent. (Note that this example is minimized to show the effect. The array would have multiple values, where for each a Child component is shown) However this example results in an endless re-rendering of the Child with the following console warning being raised throughout:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Debugging shows, that the re-rendering occurs due to onChange being changed. However, I do not understand this. Why is onChange being changed? Neither internalState nor state is changed anywhere.
There are two workarounds I found:
Remove onChange from the dependencies of the effect in the Child. This "solves" the re-rendering and would be absolutely acceptable for my use case. However, it is bad practice as far as I know, since onChange is used inside the effect. Also, ESLint is indicating this as a warning.
Using a "raw" number in the state, instead of an array. This will also get rid of the re-rendering. However this is only acceptable in this minimal example, as there is only one number used. For a dynamic count of numbers, this workaround is not viable.
useCallback is also not helping and just "bubbling up" the re-recreation of the onChange function.
So my question is: Do React state (comprising arrays) updates are being handled differently and is omitting a dependency valid here? What is the correct way to do this?
Why is onChange being changed?
On every render, you create a new anonymous function (v) => setState([v]).
Since React makes a shallow comparison with the previous props before rendering, it always results in a render, since in Javascript:
const y = () => {}
const x = () => {}
x !== y // always true
// In your case
const onChangeFromPreviousRender = (v) => setState([v])
const onChangeInCurrentRender = (v) => setState([v])
onChangeFromPreviousRender !== onChangeInCurrentRender
What is the correct way to do this?
There are two ways to correct it, since setState is guaranteed to be stable, you can just pass the setter and use your logic in the component itself:
// state[0] is primitive
// setState stable
<Child value={state[0]} onChange={setState} />
useEffect(() => {
// set as array
onChange([internalValue]);
}, [onChange, internalValue]);
Or, Memoizing the function will guarantee the same identity.
const onChange = useCallback(v => setState([v]), []);
Notice that we memoize the function only because of its nontrivial use case (beware of premature optimization).
I have a prop which is only populated the first 6 times a component renders, and then is lost. I need to save the value from this prop and make it available in the 7th and 8th renders of the component. So my thought was to do this:
function MyComponent ({ myProp, ...props }) {
const [mySavedValue, setMySavedValue] = useState();
if (myProp && mySavedValue) {
setMySavedValue(myProp)
}
...
However, this is putting a useState hook inside a condition, which is bad. It causes an infinite loop.
Error:
Uncaught Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop.
How can I save this value into a place that will persist throughout all re-renders? Is there a way to save to state without using useState hooks that will cause this problem?
If the same component gets re-rendered (that is, it doesn't get un-mounted, then re-mounted), then putting the prop into state immediately, and then referencing the stateful variable, should do what you're looking for:
function MyComponent ({ myProp: myPropParam, ...props }) {
const [myProp] = useState(myPropParam);
// reference myProp here
That said, it's pretty odd for a prop to be present initially and then to be lost. A better solution would be to change the parent component's code so that the props that get passed down are consistent.
I don't know which is your use case but if you don't want to keep in an infinite loop, what you have to do is the useEffect hook.
What's causing that infinite loop is that every time the functional component re-renders, it executes all the code inside it, hence that if statement gets executed when appropriate => sets state => re-renders => so on...
So, maybe it makes sense to you this:
useEffect(() => {
if (myProp && mySavedValue) {
setMySavedValue(myProp)
}
}, [myProp]);
Since I am assuming that the prop could change in some of the first 6 renders.
When updating state when props change, the commonly held approach is to use useEffect() with the prop as a dependency:
const Component = ({prop}) => {
const [state, setState] = useState(prop);
useEffect(() => {
setState(prop);
}, [prop]);
return <div>{state}</div>;
}
I have to wonder, is there an advantage to this over doing a comparison directly in the component itself, like so:
const Component = ({prop}) => {
const [state, setState] = useState(prop);
if (prop !== state) {
setState(prop);
}
return <div>{state}</div>;
}
It looks like both approaches cause the component to execute twice -- once with the prop and state out of sync, once in sync -- but the second approach looks like it avoids adding a hook to the stack. It also looks like it could be optimized out of the DOM reconciliation, since it doesn't have to wait for the DOM to be generated the way useEffect() does. I'm not even sure it is easier to read, other than being "The Hooks Way."
Does anyone have an idea why the useEffect() route could be better than the inline check?
The official React docs use the second approach for syncing props to state:
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
function ScrollView({row}) {
const [isScrollingDown, setIsScrollingDown] = useState(false);
const [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
The difference between updating state in useEffect and updating state during render is that, useEffect is called after React already commits the updates, i.e. the updates would be reflected in DOM, then you'll update state which will update the DOM again. The second way causes a re-render, but there's only one commit to the DOM.
I think it's important to understand when to use hooks and when not too.
The answer to this question is a very helpful How does React implement hooks so that they rely on call order.
We have recently discovered a lot of problems to do with the useEffect hook, one of them being
useEffect(() => {
// Called on first render and every props.foo update
}, [props.foo])
We didn't want it to be called on the first render only on every update.
Another on being useEffect gives warning when using objects & arrays, instead of a value.
I'm not saying don't use the useEffect ever I love the useEffect hook, but instead of saying why shouldn't use it. You should say do I need useEffect to get what I need to achieve.
For your example unless you are setting the state at another point I would suggest. Just using the prop. If you are setting the state I would have a look at the above link at the schema found.
The schema of a single hook is as below. It can be found in the implementation
function createHook(): Hook {
return {
memoizedState: null,
baseState: null,
queue: null,
baseUpdate: null,
next: null,
};
}
Think do I need to make use of the hooks queue when setting the state, if not then don't use a hook.
Another good use of the hook is are you going to be putting multiple values in the hook array if so it's a good idea to use it then!
Hope that helps :)