redux-form: Infinite loop (Maximum call stack size exceeded) when passing redux form as a prop
EDIT:
Apparently it only happens when the component where the form is being rendered is connected to the redux store and one of the properties is being enhanced on the mapStateToProps function, for example:
const mapStateToProps = state => ({
// This causes the error
clicks: { enhanced: true, count: state.clicks.count },
// This works
// clicks: state.clicks
});
Passing a redux form component as a prop to another component is causing an infinite loop. This only happens if you pass it using an anonymous function (functional component).
Here is an example:
const NewMessageForm = reduxForm({
form: 'newMessageForm'
})(({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<Field
name="text"
component="input"
placeholder="Add your message here"
/>
</Form>
));
function Wrapper({ newMessageForm: NewMessageForm }) {
return (
<div className="wrapper">
<NewMessageForm />
</div>
);
}
function handleSubmit(values) {
console.log('submit', values);
}
function Counter () {
return (
<div>
<h1>Hello World!</h1>
// Here is where the problem happens
<Wrapper newMessageForm={() => <NewMessageForm onSubmit={handleSubmit} /> }/>
</div>
);
}
This error doesn’t happen if the component is passed like this:
<Wrapper newMessageForm={NewMessageForm}/>
or even like this:
constructor(props) {
super(props)
this.NewMessageForm = () => <NewMessageForm onSubmit={handleSubmit} />
}
render() {
.....
<Wrapper newMessageForm={this.NewMessageForm}/>
.....
}
I have an example here
What’s your environment?
Tested on chrome using redux-form@6.5.0
Other informations
Stack Trace:
Uncaught RangeError: Maximum call stack size exceeded
at traverseAllChildrenImpl (traverseAllChildren.js:65)
at traverseAllChildrenImpl (traverseAllChildren.js:93)
at traverseAllChildren (traverseAllChildren.js:172)
at flattenChildren (flattenChildren.js:66)
at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:204)
at ReactDOMComponent._updateChildren (ReactMultiChild.js:312)
at ReactDOMComponent.updateChildren (ReactMultiChild.js:299)
at ReactDOMComponent._updateDOMChildren (ReactDOMComponent.js:936)
at ReactDOMComponent.updateComponent (ReactDOMComponent.js:754)
at ReactDOMComponent.receiveComponent (ReactDOMComponent.js:716)
I’ve tried to reproduce this bug in an unit test but, as for today, I wasn’t able to.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 56
- Comments: 40 (4 by maintainers)
Commits related to this issue
- Fix for form re-rendering infinitely Fix source : https://github.com/erikras/redux-form/issues/2629#issuecomment-297268290 — committed to AparnaKarve/manageiq-v2v by AparnaKarve 6 years ago
- Resolvendo bug de loop infinito aplicando a solução sugerida no link: https://github.com/erikras/redux-form/issues/2629#issuecomment-388309567 — committed to lucas-issa/simple-react-dropzone by lucas-issa 6 years ago
- Resolvendo bug de loop infinito aplicando a solução sugerida no link: https://github.com/erikras/redux-form/issues/2629#issuecomment-388309567 — committed to lucas-issa/simple-react-dropzone by lucas-issa 6 years ago
I’ve found issue.
validate
on Field element must point to the same reference otherwise given Field is reregistered. Following code caused given issue with construction that I’ve mention in comment above .I had the same issue. My form was re-rendering infinitely when the user added an object to a FieldArray.
The nextProps/this.props and nextState/this.state were all the sames in shouldComponentUpdate, but it was still re-rendering.
I was able to fix this by overriding
shouldComponentUpdate
and check if nextProps/this.props and nextState/this.state were not strictly equal.@rostislav-simonik thank you for helping me solving my problem.
Infinite loop causes:
Works fine by moving validation array outside render method, e.g.:
I did some more testing and I found what the problem really is. Basically it’s an infinite dispatch loop, what happens is that the
Field
componentdispatches
an action to register itself duringcomponentWillMount
.This will trigger react redux to check what are the components that need a rerender, and because I’m always returning a new object on
mapStateToProps
it will trigger a rerender, that will in turn mount theForm
andField
components again, and this will trigger anotherdispatch
to register theFiled
which will result on an infinite loop.I’m not sure whether this is a redux-form’s problem or not, or if there’s something we can do to prevent this error.
Maybe we can check on Field’s
componentWillMount
if it is already registered and not dispatch the action, since thecomponentWillUnmount
unregisters the component I believe this will only happens ( being register already ) when something like the problem above happens.Or maybe we can just add a check there for recursion and raise a warning about this problem pointing to some documentation explaining it and how to avoid it.
What do you think @erikras?
Hi, I was able to get rid of the infinite register/unregister loop by placing the function that is passed to
FieldArray
ascomponent
outside therender
function of the main Component:Sorry, for this absolute newby remark. I know it does not help you, but it helped me and it is worth sharing. There is not a lot of non-expert information available on redux-form.
Please look into this issue @erikras or any other redux-form maintainer. The above comment from @tiagoengel seems to be spot on for many of the cases where I’ve encountered this bug. They all appear to be caused by instantiating things passed to redux-form in the render method.
My team struggles with this issue every couple weeks and it is always a very lame time suck. The reason it is so bad is because there is no informative message about what might be going wrong. You usually have to dig for quite a while.
Even if there isn’t a way to stop the infinite render loop, at the very least we should be able to provide informative warning messages in the console about what might be causing things to loop.
Linking some related issues here as well just to point out how common of an issue this really is: https://github.com/erikras/redux-form/issues/2737 https://github.com/erikras/redux-form/issues/3581 https://github.com/erikras/redux-form/issues/2103
Is this still an on-going open issue?
It might be something subtly different, but I am seeing the same issue (Maximum call stack size exceeded) that is blowing up our
reduxForm
implementations when we try to use avalidate
method in thereduxForm
call…This works:
This blows up:
The current version has this change: https://github.com/erikras/redux-form/commit/b02e8a43486b875957359da55d1790d25b62ca92#diff-ca0d4d55e50da37455f88ffb85da436b, but the problem still almost the same. This last change will allow to create new array with the same functions (the same reference to the function), but if the same function is recreated (different reference) the problem still exists.
For example:
Will work fine:
May cause “Maximum call stack size exceeded” error:
To add some additional context. We “happen” to be using
redux-form
in the context ofReact
code loaded into aRails
app that is compiling all the ES6 code to ES5 a deploy time.Interestingly the problem had NOTHING to do with
redux-form
or even ournpm
packages at all… it was an outdated version of ouruglifier
ruby gem. Needed to go from2.7.1
to3.2.0
which also upgraded the version ofexecjs
(again a ruby gem)…Many days of hair pulling resulted in the discovery that something else unknown was causing a strange interaction. But hey, it’s working now (for us) at least! 😃
export default reduxForm({ form: FORM_NAME, validate, destroyOnUnmount: false, })(withConnect(MyComponent));
For me, destroyOnUnmount: false resolve the issue.
I have run into this issue twice. It happens when I introduce a closure in my
mapStateToProps
.The first time I just stopped using
redux-form
. Now that I have run into it again, it feels to me likeredux-form
is causing some sort of state side effect that causes unnecessary reevaluation. I can use ashouldComponentUpdate
function and see that mystate
andprops
are still equal, because the function returns the same data. It is the fact that the closure returns a function that evaluates as not equal to the previous function that breaks. I’m using selectors like this in several views to memoize data and not having issues. It is only whenredux-form
is involved that it breaks. Anyone have ideas on that?EDIT!! I found that wrapping
mapStateToProps
in amakeMapStateToProps
function is needed when using memoized selectors. This prevents a new instance of the selector from being called each time props are checked. Thus preventing infinite rendering.The webpack bin link is not working anymore for some reason. I’ve created a sample project to simulate this problem, can be accessed here (I’ve also updated the link on the original issue).
While I was creating the project I’ve found some more information about when it occurs. Apparently it only happens when the component where the form is being rendered is connected to the redux store and one of the properties is being enhanced on the
mapStateToProps
function, for example:I also found that the infinite recursion is happening here
Hey @yourGuy, Its totally possible!
If they are part of the same form the validator function will be give all the form values in the second argument and then you can use the name to look up the value of the password field and check its equal. note you the logic will be coupled to the name field
password
in this exampleIf anyone else is still having trouble with this infinite loop. You can now use the new
React.Memo
to memorize the validator. This allows you to also use values that are not inside the Form values with the validator ie values from other parts of state:I did the same as @rostislav-simonik and put a function inline as props. Once I moved the function out of the component and referenced it - the looping craziness stopped. Thank you @rostislav-simonik !
for me it happens if I use
reduxForm({ onSubmit: submit })
in decorator along with using<Form>
component. but if I change redux-form’s<Form>
component to<form>
- it works. and<Form onSubmit={handleSubmit(submit)}>
(and no submit func in decorator) works as well.I am having issues with infinite loops as well. I can reproduce by trying to call this.props.initialize in componentWillMount with undefined values, without a try/catch surrounding componentWillMount. I am using React 15.5.4, redux-form 7.0.3.
We got similar problem in our repository after a long debug.
The problem was some items of
initialValues
on the object were references passed into thereduxForm
causing that during the rendering process our object mutated forcing a new re-render over an over until we ran out of stack.After use a
_.cloneDeep
, the problem is fixed, I hope that helps to people with the same problem that I had.I’ve seen various errors in the past from dispatching during
componentWillMount
. I don’t know if it’s even supported (or if anything that causes parent comps to update is). Does anyone know for sure if it should be okay, or is only dispatching duringcomponentDidMount
the only safe option?I was running into the same problem, and reading this thread clarified my mistake.
The component rendering the form I was using was mapping state to props like this:
And I was able to fix “ignoring” the form’s state, like this:
What just happened is that I was recurring over the entire state, including the form’s state, and somehow it couldn’t manage well, leading to this infinite loop.
Fortunately, my wrapper component—the one rendering the redux form—doesn’t rely on its state and I was able to just skip it. If that’s the case of someone facing this same issue, try to just skip it as I did. Otherwise, just for a matter of statement, these
shouldComponentUpdate
solutions haven’t worked for me.