I am going through the react documentation article of integrating-with-jquery-chosen-plugin, and react gives an example of the Chosen jquery plugin and mentions the following:
Notice how we wrapped
<select>in an extra<div>. This is necessary because Chosen will append another DOM element right after the<select>node we passed to it. However, as far as React is concerned,<div>always only has a single child. This is how we ensure that React updates won’t conflict with the extra DOM node appended by Chosen. It is important that if you modify the DOM outside of React flow, you must ensure React doesn’t have a reason to touch those DOM nodes.
class Chosen extends React.Component {
render() {
return (
<div>
<select className="Chosen-select" ref={el => this.el = el}>
{this.props.children}
</select>
</div>
);
}
}
I initially thought that when react re renders <App> with new setState() call, if we don't have the extra div around the select then there would be trouble for the diffing algorithm and dom elements appended after select would be removed. But, apparently this is not whats happening. In terms of code:
<App> | |<App>
<Component1/> | | <Component1/>
<Chosen/> | -- rerender --> | <Chosen/> //gets updated and removes nodes after <select>
<Component2/> | | <Component2/>
</App> | |</App>
I tried to imitate this situation and to my surprise, even if I don't wrap select in a div, it doesn't get updated and the extra dom nodes added after select stay intact:
//class component Chosen
class Chosen extends React.Component {
constructor(props) {
super(props);
this.state = {
chosen: this.props.chosen,
};
//I haven't used this state, but even if the option tag has value as this state
//On state update, the appended red input doesn't get deleted
}
render() {
return (
<select id="unique">
<option value="one">one</option>
<option value="two">two</option>
</select>
);
}
}
class App extends React.Component {
//create state for fancybtn1 and 2
constructor(props) {
super(props);
this.state = {
fancyBtn1: "one",
fancyBtn2: "two",
};
//after 5 seconds update state
setTimeout(() => {
this.setState({
fancyBtn1: "three",
fancyBtn2: "four",
});
}, 5000);
}
//componentDidMount
componentDidMount() {
console.log("componentDidMount");
//grab #unique and append an input tag after it
setTimeout(() => { //jquery plugin like dom manipulation //$("#unique").Chosen();
const unique = document.getElementById("unique");
const input = document.createElement("input");
input.type = "text";
input.style.border = "1px solid red";
input.value = "test";
unique.after(input);
}, 2500);
}
render() {
console.log("App render");
return (
<div>
<h1>App {this.state.fancyBtn1} </h1>
<FancyBtn txt={this.state.fancyBtn1} />
<Chosen chosen={this.state.fancyBtn1} />
<FancyBtn txt={this.state.fancyBtn1} />
</div>
);
}
}
let FancyBtnCount = 0;
//create FancyBtn class based component which has state for txt
class FancyBtn extends React.Component {
constructor(props) {
super(props);
this.state = {
txt: this.props.txt,
};
//set counter, which keeps track of how many times the component is instantiated
//I couldn't do it properly atm.
FancyBtnCount++;
}
//on component update
componentWillReceiveProps(nextProps) { // I know gives deprecated warning
console.log("componentWillReceiveProps");
//set state if prev state is different
if (this.state.txt !== nextProps.txt) {
this.setState({
txt: nextProps.txt,
});
}
}
render() {
return (
<div>
<h1>FancyBtn {FancyBtnCount} </h1>
<input type="text" value={this.state.txt} />
<button>{this.state.txt}</button>
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>After 2.5 seconds I add a red input dom node after select and after 5 seconds, I re render the whole app. Now why doesn't with the re render the react diffing algorithm remove the red input? What difference would it make if I wrap the select in an extra div? Basically I am looking for an explanation of the quoted react docs.
from Why should dom element be wrapped in an empty div while integrating jquery plugin in react Js?
No comments:
Post a Comment