Sunday, May 19, 2024
 Popular · Latest · Hot · Upcoming
88
rated 0 times [  95] [ 7]  / answers: 1 / hits: 148440  / 8 Years ago, fri, march 4, 2016, 12:00:00

Lets say I have a view component that has a conditional render:



render(){
if (this.state.employed) {
return (
<div>
<MyInput ref=job-title name=job-title />
</div>
);
} else {
return (
<div>
<MyInput ref=unemployment-reason name=unemployment-reason />
<MyInput ref=unemployment-duration name=unemployment-duration />
</div>
);
}
}


MyInput looks something like this:



class MyInput extends React.Component {

...

render(){
return (
<div>
<input name={this.props.name}
ref=input
type=text
value={this.props.value || null}
onBlur={this.handleBlur.bind(this)}
onChange={this.handleTyping.bind(this)} />
</div>
);
}
}


Lets say employed is true. Whenever I switch it to false and the other view renders, only unemployment-duration is re-initialized. Also unemployment-reason gets prefilled with the value from job-title (if a value was given before the condition changed).



If I change the markup in the second rendering routine to something like this:



render(){
if (this.state.employed) {
return (
<div>
<MyInput ref=job-title name=job-title />
</div>
);
} else {
return (
<div>
<span>Diff me!</span>
<MyInput ref=unemployment-reason name=unemployment-reason />
<MyInput ref=unemployment-duration name=unemployment-duration />
</div>
);
}
}


It seems like everything works fine. Looks like React just fails to diff 'job-title' and 'unemployment-reason'.



Please tell me what I'm doing wrong...


More From » reactjs

 Answers
6

What's probably happening is that React thinks that only one MyInput (unemployment-duration) is added between the renders. As such, the job-title never gets replaced with the unemployment-reason, which is also why the predefined values are swapped.


When React does the diff, it will determine which components are new and which are old based on their key property. If no such key is provided in the code, it will generate its own.


The reason why the last code snippet you provide works is because React essentially needs to change the hierarchy of all elements under the parent div and I believe that would trigger a re-render of all children (which is why it works). Had you added the span to the bottom instead of the top, the hierarchy of the preceding elements wouldn't change, and those element's wouldn't re-render (and the problem would persist).


Here's what the official React documentation says:



The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key.


When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).



You should be able to fix this by providing a unique key element yourself to either the parent div or to all MyInput elements.


For example:


render(){
if (this.state.employed) {
return (
<div key="employed">
<MyInput ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div key="notEmployed">
<MyInput ref="unemployment-reason" name="unemployment-reason" />
<MyInput ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}

OR


render(){
if (this.state.employed) {
return (
<div>
<MyInput key="title" ref="job-title" name="job-title" />
</div>
);
} else {
return (
<div>
<MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
<MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
</div>
);
}
}

Now, when React does the diff, it will see that the divs are different and will re-render it including all of its' children (1st example). In the 2nd example, the diff will be a success on job-title and unemployment-reason since they now have different keys.


You can of course use any keys you want, as long as they are unique.




Update August 2017


For a better insight into how keys work in React, I strongly recommend reading my answer to Understanding unique keys in React.js.




Update November 2017


This update should've been posted a while ago, but using string literals in ref is now deprecated. For example ref="job-title" should now instead be ref={(el) => this.jobTitleRef = el} (for example). See my answer to Deprecation warning using this.refs for more info.


[#63051] Wednesday, March 2, 2016, 8 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
albert

Total Points: 652
Total Questions: 105
Total Answers: 108

Location: Pitcairn Islands
Member since Fri, Oct 15, 2021
3 Years ago
;