I have seen a lot of questions regarding updating a parent's state from a child component, however my question is just the opposite. I have a component, a modal editing window, used for modifying objects stored in a parent's state.
I only want to have these changes copied to the parent if the user saves these changes, which is handled by a function I pass to the child component.
Currently, anytime I make a change to the prop.obj in the child, the change is picked up by the parent on re-render.
here is an example of one of my change handlers
handleChange(e) {
this.state.childobj.myvaluetochange = e.target.value
}
In my parent, as stated, I pass obj to the child from the parent's state like so
<MyComponent obj={this.state.obj} />
to summarize:
I send a state object to a child
The child uses this.props.obj to use this obj in a form and uses handlers to update
When changes are picked up in the child, they reflect on the parent's state object on update - I don't want this unless they choose to save. (this is handled by a function passed in from the parent)
to clarify, I want the state obj passed to the child to act as a completely separate object when it lives in the child. Is this possible?
Thanks
This is happening because you are mutating the given prop.
To avoid this you should create new instance of "this.props.obj". Probably you want to do it in "componentWillMount" and "componentWillReceiveProps" lifecycle methods. To create new instance of object you can use Object.assign or spread operators.
You are creating a link to the parent object and then mutating the state which will reflect in the parent. An easy way around this is to create a new version of it by using the spread operator to create a new object:
this.state = {childObj: {...this.props.obj}}
Related
At the top of my component I define a variable with useRef like below.
const data = {
item1 : useRef (null),
}
console.log (data['item1'].current?.srcObject)
Now as the logic proceeds through the component the value for item1 is set up. The ref is attached to a video element. And at certain points the component re-renders.
The console.log prints the value of the srcObject upon the first re-render and the second. But on the third that value is lost. The srcObject is not explicitly changed in between the re-renders.
I don't fully understand refs but I'd like to know how these values are retained or destroyed. Of course I get that they maybe case specific but I'd still like to get the general idea.
You probably want to instead do const ref = useRef({item1: ...}) and reference the current value as ref.current.item. Your object is getting re-created every render.
If you put the object inside the ref, ref.current will be the object, and that object won't update or change with react renders. That's what refs are for, values that you can update independent of rendering, that also stick around for the lifetime of the component.
Im using React. and trying to assign an new key value. pair into the object. So first it goes through an if check and if it meets the requirement, I want to add the key value 'author'
if (field.component === 'Author') {
this.props.writer.config.payload.name = 'Jefferson';
console.log(this.props)
}
There are some online articles that tell me to do this way and others that tell me to do Object.assign. Basically though, I just want to add the 'name':'Jefferson' into the object.
See this answer https://stackoverflow.com/a/24943743/1964636
Mutating props in a child component is an anti pattern,
You may want to pass a function as props that can lift state in the child component.
Once you modify the state of it's parent component (assuming the props are part of the parent component's state), it will rerender with updated props.
I noticed something strange with prop mutation (Vue 2.6).
Mutating props directly in a child component should be avoided and will trigger the following famous warning
"Avoid mutating a prop directly since the value will be
overwritten...."
So if in my parent component I have a data like
exemple: "Hello World"
that I passed down as a prop to a child component. If in that child component I do something like
this.exemple = "Hello World!"
I get a warning. Fair enough. Now I noticed that if the data in the parent is an object like
exemple : {message : "Hello World"}
and that in the child I do something like that
this.exemple.message = "Hello World!"
This does not trigger any warning and moreover the data in the parent component get updated
I'm wondering why. Why the prop mutation propagate to the parent in one case but not in the other ? Does it have maybe something to do with how javascript store these variable ? Is it good practice to use that trick ?
Why does the prop mutation propagate to the parent in one case but not in the other ? Does it have maybe something to do with how javascript stores these variable ?
Yes. JavaScript reference variables such as objects and arrays don't change their references when you mutate their properties/values. This means the parent can access the changed properties too, because it also has access to the reference. And Vue only gives a prop mutation warning if the reference changes.
Warning:
this.prop = {}
No warning:
this.prop.id = 5
Is it good practice to use that trick ?
No. It's back to violating One way data flow in which data is passed down through props (to child) and up through events (to parent).
Ok, but why doesn't Vue warn about those changes?
Consider an object with 1000 properties or an array with 1000 items. Vue would have to deep check all 1000 for changes, and there is no quick way to do it because each item has to be individually compared to the old value. This would have performance implications and probably make many apps unusable.
Here is a comment on the subject from Vue team member Bolerodan:
Generally you shouldnt do that. This can / could cause unexpected bugs in your code. Generally you should make a copy of your object, modify that, then emit upwards to the parent (as you have mentioned in your last paragraph I believe). This is the method I take and is the general consensus of top down, emit up approach.
A very simple answer is that You are mutating a reference value and Vue doesn't track the whole reference instead it only checks for the address where the reference is. So if you re-assign that value then it will warn you.
A good example to understand the behaviour is understanding through variable declaration const
const makes your variable immutable but again same applies to it if the value is primitive in nature you can't change it, but if it's a reference you can't change it but you can definitely update a value located at the reference
const a = 'Foo' // Primitive value
a = 'bar' // voilation
const b = { // Reference value
name: 'Foo'
}
b.name = 'Bar' // It's good
console.log(b)
b = {} // re-assign is voilation
Because the mutation prevention only catches direct assignments to the prop - it does not catch manipulations of its properties. The idea is that if you assign new values to the prop - you are assigning to the prop and not to the variable that is sitting in the parent Vue component which feeds the prop.
As soon as something in the parent component updates the feeding variable - the prop will be updated with this new value and the value that you have assigned to the prop in your child component will be overwritten.
If you feed the prop with an object and then mutate a property inside the prop in your child component - it will affect the same object inside the parent component. But if you try to assign a new object to the prop in your child component - you should get the same warning about mutating props.
class Parent extends React.Component {
handler = (event) => {
this.setState(state => ({ count: state.count + 1 }));
}
render() {
return (
<div>
{this.state.count}
<Child Parent={this} />
</div>
);
}
}
function Child(props) {
return <button onClick={props.Parent.handler}>increment count</button>
}
I have used this type of pattern a few times, didn't find any consequence, does anyone see any harm following such a pattern?
sometimes there are so many properties to be sent
In my opinion, if Child use almost all properties in Parent, maybe:
Child is Parent ?
split Child to multi Child like Child1、Child2 ?
And, if you pass Parent to Child, it's not semantic. I cannot aware what properties that Child needed when I use it elsewhere.
Looking forward to more discussion. :)
I am adding some benefits we get with this pattern, and I assume with my limited knowledge that this is ok
we can access props and state of Parent directly in child
can change parent state from child, using Parent.setState
can access other functions defined on the Parent class and thus reduce passing down everything as props
I feel this is a much better solution, because Parent reference never changes and therefore, it does not cause unnecessary rerender of child, and we do not need to memorize Parent' function references that we pass to child
While an argument of code smell may be raised by OO Specialist, that we are allowing child to modify properties of parent, we can safely ignore it for React, since we are anyway passing whole redux store to every component and any change in redux store, impacts so many components simultaneously.
---- following is an except from same question i posted on freecodeCamp. ------
Regarding code smell, only because one class is modifying other class’s properties. We have to think twice.
the whole concept of state is there so that it can be modified internally,
whether a class itself modifies or it gives right to some child to modify – it’s kind of similar thing, it’s only delegation that is the addition
Always to note, that the parent has full control over child. if child is not acting as per parent, the child can be replaced by parent.
To add one more point, we are anyways giving every component the right to modify whole redux store by useDispatch, thus argument of code smell doesn’t hold so strong for React per se
https://forum.freecodecamp.org/t/is-it-ok-to-pass-class-component-reference-to-child-component-in-react/408341/7
I have an autocomplete component which has following props:
value (display text),
onChange (fired on every keystroke)
onSelect(fired when user picks something from the list of
suggestions, passes selected object + display text of selected object - the same as value)
state:
- suggestionsList (I've decided it's a state as it's something internal to the component).
On one of the views parent of this component doesn't care about the value after each keystroke. It just cares about the selected item passed by onSelect handler. So in this scenario value & onChange props are not needed. value could become an internal state of the autocomplete component.
But on one of the other views parent would like to also know about each keystrokes. This means the parent would need to hold the value in his state and also pass it as props. onChange props would be also needed to notify parent to change its state.
How to construct the autocomplete component so it handles both scenarios? Which properties should be props and which state?
Any suggestions appreciated.
In the second case, if the parent needs to know about the current value, it doesn't need to hold the variable, so I guess that if you pass the current value (from an internal state variable) as a parameter to the onChange prop function, the parent will notice the change and update accordingly.
Another way to do it is to use the value property and a internal state variable. What I mean is: if the parent wishes to control the value it will have an state entry for it and will be responsible for updating it, in other case the value property must not be provided because it would end as a constant. Your component will use this property as the current value every time its provided, and the parent should use onChange to update the va;ue that it holds.
You can check the source code of the auto complete component in material-ui.