-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nested fields objects are mutating #375
Comments
If I'm doing like this: render() {
const { fileds: { someField, mainProduct } } = this.props;
return (
<div>
<input {...someField} />
<MainProductInput fields={ {...mainProduct} } />
</div>
);
} Everything works perfect. |
Hmm... It is supposed to be creating new instances for any part of the subtree that changes. Will investigate... 🔍 |
We are facing the exact issue |
Is this still an issue in v4? |
@erikras I've just checked it - seems issue still here |
@erikras anything I can do to help you figure this one out? |
Just found this as well
I'll try my hand at a pull-request now and see how I go :) |
^^ Look at that test. (it passes) What's wrong about it? |
When I get isEqual = true and I've changed a field that's bad. It means that the field has mutated rather then create a new pointer that I can do a quick reference check against. One thing I am noticing is that if I change the field so it isn't nested ie just email then I get |
Okay, but you looked at the test, right? The field that isn't changed is still |
@iNikNik Very good. That's a bug for sure. Perhaps that's what was originally being referenced. 👍 |
do you think fixing this will help with #529? |
@luisrudge Probably not, since this mutating bug is preventing rerendering. Fixing this will result in more rendering. |
Damn :D |
But it will allow us to wrap components in purerendermixin so only the components that change will be re-rendered. |
Oh and sorry I didn't look at the test. Felt a bit flakey last night so went to bed early. Hopefully tonight I'll get some time. |
Fix published as |
Perfect! Thanks mate you are awesome |
@erikras Sorry to bring bad news... :( shouldComponentUpdate(nextProps) {
console.log(this.props.prescription.comment.value === nextProps.prescription.comment.value); // always true
console.log(this.props.prescription === nextProps.prescription); // always true
} When I update comment, both I use {this.props.fields.prescriptions.map((prescription, index) =>
<Prescription
key={index}
prescription={prescription}
index={index}
/>
)} I use the latest release. And when I check values, |
Ok, I wrote a spec for both issues. @erikras Can you have a look? it('should not mutate tree or change untouched nested values in arrays', () => {
const store = makeStore();
const form = 'testForm';
const Decorated = reduxForm({
form,
fields: ['phones[].prefix', 'phones[].number']
})(Form);
const dom = TestUtils.renderIntoDocument(
<Provider store={store}>
<Decorated/>
</Provider>
);
const stub = TestUtils.findRenderedComponentWithType(dom, Form);
stub.props.fields.phones.addField();
stub.props.fields.phones.addField();
const phones = stub.props.fields.phones;
const values = stub.props.values;
const phone0 = phones[0];
const phone1 = phones[1];
phone0.number.onChange('555-1234');
expect(stub.props.values).toNotBe(values); // works
expect(stub.props.values.phones).toNotBe(values.phones); // works
expect(stub.props.values.phones[0]).toNotBe(values.phones[0]); // works
expect(stub.props.values.phones[1]).toBe(values.phones[1]); // fails. useless update?
expect(stub.props.fields.phones).toNotBe(phones); // fails. tree mutation?
expect(stub.props.fields.phones[1]).toBe(phone1); // good, but...
expect(stub.props.fields.phones[0]).toNotBe(phone0); // fails. tree mutation?
}); I guess if you can make that test pass, you would be some kind of Pure-Render superhero. |
@erikras Hi! Right now you mutate object primitives, for So this evaluates to true: Should fields and values be new objects/arrays on their part change, like you will do in the reducer on state change?
This will allow pure rendering, and also passing whole deep form slices to components. Right now I am supposed to render whole fields in wrapped component. Btw, props.values are changed on focus/blur events, should they? Imo they should be new object for corresponding path only if value in this path changed, like fields above. P.S. Sorry if this is already some where in issues, I did not found them. |
@erikras You're the man, and thanks for this library! Would you mind addressing the two questions above please? Was this fixed in a later release? I'm still on version 4 and getting ready to update, but want to make sure if the SPEC @bbenezech would pass or @Coobaha 's question. My app slows down because everything must re-render since the fields array shows it remains equal. |
Yet another instance of issue redux-form#375. Specifically, any array that contained non-immediate descendants (e.g. arrayField[].deepObjectField, or arrayField[][], but not plain old arrayField[]) would remain the same instance if any of its descendant fields were updated but no items were added to or removed from the top-level array. The issue was that we were only making a fresh copy of the array (that's `changed = true`) in the case where it a raw-value array item was changed, but not in the general case.
Yet another instance of issue redux-form#375. Specifically, any array that contained non-immediate descendants (e.g. arrayField[].deepObjectField, or arrayField[][], but not plain old arrayField[]) would remain the same instance if any of its descendant fields were updated but no items were added to or removed from the top-level array. The diff doesn't quite capture what the changes in behavior are, so: - if there's a change in a deep array field, we set changed = true unconditionally, instead of just in the non-object-member case. This ensures that the array itself is cloned instead of just mutated, and hence that the old array compares !== the new array. - if there's a change in an object member of a deep array field, we make clone the top-level object and assign it to the array member, instead of just accepting the mutation made by the recursive call to readField(). This ensures that the old object item in the array compares !== the old object.
Yet another instance of issue redux-form#375. Specifically, any array that contained non-immediate descendants (e.g. arrayField[].deepObjectField, or arrayField[][], but not plain old arrayField[]) would remain the same instance if any of its descendant fields were updated but no items were added to or removed from the top-level array. The diff doesn't quite capture what the changes in behavior are, so: - if there's a change in a deep array field, we set changed = true unconditionally, instead of just in the non-object-member case. This ensures that the array itself is cloned instead of just mutated, and hence that the old array compares !== the new array. - if there's a change in an object member of a deep array field, we make clone the top-level object and assign it to the array member, instead of just accepting the mutation made by the recursive call to readField(). This ensures that the old object item in the array compares !== the old object.
Yet another instance of issue redux-form#375. Specifically, any array that contained non-immediate descendants (e.g. arrayField[].deepObjectField, or arrayField[][], but not plain old arrayField[]) would remain the same instance if any of its descendant fields were updated but no items were added to or removed from the top-level array. The diff doesn't quite capture what the changes in behavior are, so: - if there's a change in a deep array field, we set changed = true unconditionally, instead of just in the non-object-member case. This ensures that the array itself is cloned instead of just mutated, and hence that the old array compares !== the new array. - if there's a change in an object member of a deep array field, we make clone the top-level object and assign it to the array member, instead of just accepting the mutation made by the recursive call to readField(). This ensures that the old object item in the array compares !== the old object.
Published fix in |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Hello there! I found some little bug - nested fileds are mutating when you change something:
I mean if you pass down whole nested field object - your children component will never rerenders.
I believe we should map
myFields
values and create a new objects for all nested fields here ->https://github.com/erikras/redux-form/blob/master/src/readFields.js#L25
PS guys, I want to tell you really big thanks! You're doing nice job with redux-form)
The text was updated successfully, but these errors were encountered: