Open ThaNarie opened 4 years ago
I am trying to read outside of the form the state saved in Redux and I got the same warning :
Related issue: #828
Is there a workaround or am I stuck with it?
@erikras Please any help on this? While the changes you published to #751 seem to work, I don't think it covers setting state inside the onChange function in a FormSpy.
Thanks.
The issue still persists with react-final-form@6.5.1
and final-form@4.20.1
with react version 16.13.1
. Here's what I've found after digging through the (compiled) library source:
useField()
is invoked internally by the react-final-form
library from within the Field
component.useState()
hook. This parametrizes the internal register()
function with a true
second argument which is the silent
flag. All is fine at this point.final-form
goes into a validation loop. I see in the stack trace that it invokes runValidation()
which in turn leads to notifyFieldListeners()
and then to notifySubscribers()
on each field. This actually brings the code path back to react-final-form
, into the useEffect()
call inside of which the field is registered again but this time without the silent
flag.React.useEffect(function () {
return register(function (state) {
if (firstRender.current) {
firstRender.current = false;
} else {
setState(state);
}
}, false);
}
I think the following happens: from the aspect of one particular field, registration indeed happens properly (first with the silent and then without the silent flag) but other fields could have completed this cycle already. So, when a field gets initialized it runs into the validation loop, causing the setState()
call to take place inside other fields, thereby violating the rule.
I have no suggestion at this point as to how it could be solved though.
Does someone already have a solution or workaround for that problem?
In my case this error was caused by a mutator, which was called on every FormSpy
render.
I've solved this problem by wrapping the mutator call inside a useLayoutEffect
and now everything works like before.
Does anyone have an example solution to this?
Hi all, I have been battling this issue while migrating from Redux-Form to React-Final-Form. Redux-Form has isDirty() in its API so I could easily get the state of a Form and use it in another (external) component. The only similar thing I can find for Final Form is FormSpy ( if not please let me know! ). In the end I had to give up and push the "dirty" flag into redux (in effect, rolling my own isDirty() for final-form) : which is when I bumped into the issue you are talking about here.
So far the only way I can get rid of the Warning is by wrapping the redux dispatch
call in a cheeky setTimeout(,0)
. Hacky, stochastic, but it is working at least. Defo not a solution long term but thought I'd share :)
// passed to FormSpy's onChange
onChange = ({dirty}) => {
// dodgy workaround to avoid Warning :P
setTimeout(()=>this.props.dispatch( formActions.changed({dirty}) ) ,0)
}
I am experiencing the same issue in our project. I can help with investigation and report error details if needed.
@ThaNarie I hope you get rid of the error when you write it like this. this is how i solved it
<FormSpy
subscription={{ values: true,modified: true, }}
onChange={change => {
// fired during rendering, calling a useState
setter fails
if(Object.keys(state.modified).length > 0){
setValues(change.values);}
}}
/>
In my case this error was caused by a mutator, which was called on every
FormSpy
render.I've solved this problem by wrapping the mutator call inside a
useLayoutEffect
and now everything works like before.
Could you please give an example?
Seems like one of the solutions can be creating your own Spy
component in such a case, e.g. simple implementation could look like this:
import { VFC, useEffect } from 'react'
import { useFormState } from 'react-final-form'
type Props = {
onChange: (props: any) => void
}
const component: VFC<Props> = ({ onChange }) => {
const state = useFormState()
useEffect(() => {
onChange(state)
})
return null
}
export default component
Little react hook: don't have a real onChange handler until after the first render is called.
function useAfterFirstRender(func) {
const [retFunc, setFunct] = useState(() => ()=>{});
useLayoutEffect(() => {
setFunct(() => func);
}, []);
return retFunc;
}
Hi guys,
Is there any fix for this issue on the horizon? We are experiencing the same error; Although, our stack involves slightly more moving parts so it is difficult to diagnose correctly where the issue arises:
react-final-form: ˆ6.5.7
react-final-form-arrays: ˆ3.1.3
@dnd-kit/sortable: 7.0.0
Also various ui component libraries.
As far as I understood, the issue is caused by final-form
setting state while rerendering?
Are there any news from the lib maintainer?
Please see #985 (comment)
That is a different issue
wrapping the callback in requestAnimationFrame
solved the problem for me
<FormSpy
subscription={{ values: true }}
onChange={(state) => {
requestAnimationFrame(() => {
// your callback body
});
}}
/>
For me, creating a new component that only fires the event if the page has rendered did the job:
import { useEffect, useRef } from "react";
import { FormSpy } from "react-final-form";
export const FormSpyWhenReady = ({
subscription,
onChange,
}: {
subscription: { values: boolean };
onChange: () => void;
}) => {
const readyRef = useRef(false);
useEffect(() => {
readyRef.current = true;
}, []);
return (
<FormSpy
subscription={subscription}
onChange={() => {
if (readyRef.current) {
onChange();
}
}}
/>
);
};
Are you submitting a bug report or a feature request?
bug report
What is the current behavior?
When using
FormSpy
, theonChange
is called during rendering, and throws an error if you usesetState
.What is the expected behavior?
No error.
Sandbox Link
https://codesandbox.io/s/react-final-form-formspy-hltnv?file=/src/App.js
What's your environment?
Other information
The issue seems to be that
onChange
in theuseFormState
is called in theuseState
init function, which means during rendering: https://github.com/final-form/react-final-form/blob/464f1c7855e93899630df0ad897c322995601849/src/useFormState.js#L26This is part of a range of error messages introduced in react 16.13, and related to an existing issue (https://github.com/final-form/react-final-form/issues/751)
A workaround would be to put any
setState
on theonChange
in asetTimeout(... ,0)
, but this feels like a hack, and not clear to new users.