React unmount and remount child component - reactjs

I have an npm imported component (CKEditor) that only cares about the state of its parent component at the time it mounts. I.e., no matter what changes occur in the state of the parent component, CKEditor won't reflect those changes if it has already mounted.
This is an issue for me because I need CKEditor to change based on the state of the parent component when the parent component changes its language prop.
Is there a way for me to make a child component unmount and mount again from the parent component? For example, is there a way for me to unmount and mount again a child component based on the parent component's "componentWillReceiveProps"?
import React from 'react';
import CKEditor from "react-ckeditor-component";
export default class EditParagraph extends React.Component {
constructor(props) {
super(props)
this.state = {
// an object that has a different html string for each potential language
content: this.props.specs.content,
}
this.handleRTEChange = this.handleRTEChange.bind(this)
this.handleRTEBlur = this.handleRTEBlur.bind(this)
}
/**
* Native React method
* that runs every time the component is about to receive new props.
*/
componentWillReceiveProps(nextProps) {
const languageChanged = this.props.local.use_lang != nextProps.local.use_lang;
if (languageChanged) {
// how do I unmount the CKEditor and remount it ???
console.log('use_lang changed');
}
}
handleRTEChange(evt) {
// keeps track of changes within the correct language section
}
handleRTEBlur() {
// fully updates the specs only on Blur
}
getContent () {
// gets content relative to current use language
}
render() {
const content = this.getContent();
// This is logging the content relative to the current language (as expected),
// but CKEditor doesn't show any changes when content changes.
console.log('content:', content);
// I need to find a way of unmounting and re-mounting CKEditor whenever use_lang changes.
return (
<div>
<CKEditor
content={content}
events={{
"blur": this.handleRTEBlur,
"change": this.handleRTEChange
}}
/>
</div>
)
}
}

Since CKEditor only uses "content" prop when it mounts, I needed to re-render the component when the local.use_lang of the parent component changes.
CKEditor can be forced to re-render by giving it a key prop equal to the value that should force it to re-render:
<CKEditor key={this.props.local.use_lang} etc />
This way, whenever the language prop changes, React creates a new CKEditor.
Keep in mind that I made use of this solution because CKEditor is a foreign component library that I installed with npm. If it was my own code I was working with, I would just make changes to how the Editor uses its props. However, since I refuse to make changes to foreign library code, this is the solution that allowed me to force a re-render without having to touch the internals of the Editor code.

Its because there is no change detected, so therefore it will not call render() again, and thus will not make another call to getContent().
What you can do is have the content be part of the state (which according to your constructor, already is), and check in componentWillReceiveProps() if the use_lang is updated. If so, then you can update the state there by calling this.setState({...rest, content = getContent()};.
And then your component render() function should look like this
<CKEditor
content={this.state.content}
events={{
"blur": this.handleRTEBlur,
"change": this.handleRTEChange
}}
/>
(As an aside, by calling setState(), this will in turn trigger a call to render(), and if any changes were detected, the changes will be shown. But note that this is not actually 'remounting' the component, it is merely updating the view. In other words, componentWillMount() and componentDidMount() will not be called after updating the state in this situation. Instead, componentWillUpdate() and componentDidUpdate() will be called). Read more about the component lifecycle here

Related

Is that possible that a component will also be re-rendered without props and states changing

In React, if father component "A" re-rendered and change the position of its son component "B", but B's props and states aren't changed, then will the "B" be re-rendered? If it will, why is that necessary?
Below is code in parent component, and "Poker" is the child. "this.props.localPokers" is an array which can be added element by clicking a button:
the position of bottom poker has changed
Well, I insert console.log in child render(), it does re-rendered the DOM, so I think the question now becomes if the updating of the child is necessary?
I try to give an answer, with an example that can show what I'm saying. Though, you may want to wait for other people to give other answers too.
Short story: yes, the child Component will be re-rendered, and that's because, being a child of a Component that is being re-rendered, its render() method will be called because of a sort of "waterfall effect": each Component inside a render() method can be seen as a function, thus, if the render() method is called, all the functions inside it are called again, leading to a re-render.
Though, what's important is that, even if the child Component is re-rendered, this does not mean that the DOM will change! Actually, that will not happen, and that's because of the React reconciliation: https://reactjs.org/docs/reconciliation.html.
Basically, React is smart enough to see if something has changed in the DOM, and replace the DOM element that actually needs to change (this is really semplified).
Now, about the example, look at this fiddle:
class Child extends React.Component {
/* Un-commeting this function, you can see that the render() method is not called agian.
shouldComponentUpdate(nextProps, nextState) {
if (JSON.stringify(nextProps) === JSON.stringify(this.props) &&
JSON.stringify(nextState) === JSON.stringify(this.state)) {
return false;
}
return true;
}*/
render() {
console.log("Child's rendering");
return (
<p>Child says "Hello World"</p>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {show: false};
}
render() {
return (
<div>
<button onClick={() => this.setState({show: !this.state.show})}>Toggle</button>
{this.state.show && <p>Parent says: Hello World!</p>}
<Child />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
#import url(https://fonts.googleapis.com/css?family=Montserrat);
body {
font-family: 'Montserrat', sans-serif;
}
<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='root'></div>
Each time you click on the button, in the console will be rendered the message Child's rendering, thus the Child Component is running its render() method.
BUT! If you inspect the DOM, when you click the button and the message `Parent says: Hello World" appears on the screen, this is what's happening in the DOM:
As you can see, only the <p> element with the message is changing in the DOM!.
Instead, when you click again the button and the message goes away, this happens:
In this case is the parent <div> element that is changing, and only it, and that's because we have deleted one of its child.
Its necessary Because we’re working with JavaScript, we can change children. We can send special properties to them, decide if we want them to render or not and generally manipulate them to our will.
You can use React's PureComponent if don't want to update Child Component every time when Parent Component updated
You can use shouldComponentUpdate to prevent a rerender of the child component. It can be used to prevent component renderings on a fine-grained level:
You can apply equality checks for different props and state, but also use it for other kind of checks. However, imagine you are not interested in checking each incoming prop by itself, which can be error prone too, but only in preventing a rerendering when nothing relevant (props, state) has changed for the component. That’s where you can use the more broad yet simpler solution for preventing the rerender: React’s PureComponent.
import React, { Component, PureComponent } from 'react';
...
class Square extends PureComponent {
render() {
return <Item>{this.props.number * this.props.number}</Item>;
}
}
React’s PureComponent does a shallow compare on the component’s props and state. If nothing has changed, it prevents the rerender of the component. If something has changed, it rerenders the component.

Is it possible to control a child component in react if the parent component render many times?

I am working on the issue regarding parent component render twice and child component also render twice. the existing code for the parent is
<childform
value ="name"
serverapi={this.valueservice.api}
update={this.update}
/>
the child component start render using
componentDidMount()
{
this.callservice(serverapi)
}
since its componentDidMount function called twice, the API also render twice which has to be avoided, each time the parent render the child's state is been refreshed so I cannot compare with the state, is it possible anyway to check or solution to this issue that I can refer? that resolves how many time I call the parent it call the API once
It seems that the update prop you are passing to childform is a boolean indicating whether or not the component should rerender. If that is the case you can use the shouldComponentUpdate() lifecycle method the to check if there is an update and only rerender when true:
shouldComponentUpdate(nextProps, nextState) {
//You can perform more logic here such as
//return this.props.update === nextProps.update
return nextProps.update;
}
You can find more information here:
https://reactjs.org/docs/react-component.html#shouldcomponentupdate
You can have a global variable called cache.
What this cache do is to check if your_awesome_api is called before.
This code below is a common approach:
// cache.js
let cache = {};
export default cache;
// someComponent.js
import cache from './cache';
// ...ignore some react component code...
componentDidMount() {
if (cache['your_awesome_api']) {
doSomething(cache['your_awesome_api'])
} else {
this.callservice(your_awesome_api).then(result => {
cache['your_awesome_api'] = result
})
}
}
This way, you don't need to worry about re-rendering problem ever!
Re-rendering in react is really common!

When shouldComponentUpdate / render is called for child components of updated component

Having read a bunch of articles on the web about shouldComponentUpdate & render I want to double check if I get it correct when components are rerendered (render method is called) and when shouldComponentUpdate is called.
React docs (and dozens or articles say) that shouldComponentUpdate is called ONLY when new props are received or there is new state. But is seems that PureComponent does the same at first glance...
So to investigate it I wrote sample app:
parent component
import React, { Component } from 'react';
import './App.css';
import Hello from './Hello';
class App extends Component {
objWithName = { name: 'World' };
state = {
date: Date.now()
}
componentDidMount() {
setInterval(() => {
this.setState({ date: Date.now() });
}, 2000);
}
render() {
return (
<div className="App">
<p>Rendering timestamp {this.state.date}</p>
<Hello name={this.objWithName} />
</div>
);
}
}
export default App;
child component
import React from 'react';
export default class Hello extends React.Component {
shouldComponentUpdate() {
console.log('shouldComponentUpdate called');
return true;
}
render() {
console.log('render called');
return <p>Hello {this.props.name.name}</p>
}
}
So my first question is: is it completely true? I mean: In above code snippets, parent component in setInterval calls setState just to trigger its update, but this state is not used anywhere. And after doing that, child component is rerendered (render is called) & shouldComponentUpdate is called even though nothing has changed for him (it didn't receive any new props, nor state). I didn't find any explanation for this behaviour in React docs so I'm not sure how it works. What is more, if that child component didn't have any input props at (simply render static string) it would also get rerendered. Can sb explain it?
So my second question is: what does it mean new props/state is received by the component? Does it mean that object value is changed (for primitives simply new value, and for objects new reference)?
Third thing: assuming that a change in parent top most component (e.g. App.js) in the application happens (new prop or new state), does it mean that by default ALL react component that are currently rendered/mounted (even leafs that do not have any state, nor props that were changed) will rerender?
shouldComponentUpdate is just called from setState and forces render when returns true (default behaviour) - and updating DOM when different. This is by design to have a lifecycle/flow. When shouldComponentUpdate returns false then you can avoid unnecessary, usually costly render processing (and DOM update). PureComponent is just an optimized, ready to use component with shouldComponentUpdate defined to shallowly compare props (by comparing references).
React doesn't care if props/state are used, not using observables - you can use mobx for that.
New props means any change - it's shouldComponentUpdate responsibility to check swallowly or deep, depends what you need.
Yes, all children will be updated (forced to refresh node state/view in virtual DOM). It's fast/optimized process (operating on virtual tree) but they should use shouldComponentUpdate to limit theirs rerendering (and final DOM updates).

Should updating props re-render the entire component?

Let's say I have a CookingClass component that gets initialized like this.
let teachers = makeTeachers(["Amber", "Jason", "Lily"])
let students = makeStudents(["Hopper"])
<CookingClass
teachers={teachers}
students={students}
/>
One of the teachers dropped out:
let newTeachers = makeTeachers(["Amber", "Jason"])
<CookingClass
teachers={newTeachers}
/>
It will make the entire component re-render. I am not sure whether React only calculates the diff and efficiently re-renders it or I must use shouldComponentUpdate to take care of it myself.
More real-world example might be implementing a Google map where there are a million markers and you want to replace one of the markers.
You're changing a so called Virtual DOM node. For every change in a virtual node shouldComponentUpdate() gets called. If you don't implement it yourself it will always return true;
So if you only want to reload your CookingClass in specific cases you would have to implement it yourself.
The pro of React is that it will only re-render Native DOM nodes when they will get changed in the Virtual DOM. This is the "render" which makes React so fast.
Based on your sample code, the component will re-render everytime.
You should use the react-redux (documentation) bindings to "connect" the component to the store.
// ConnectedCookingClass.js
import { connect } from 'react-redux';
import CookingClass from './CookingClass';
const mapStateToProps = (state) => {
return {
teachers: state.teachers,
students: state.students
};
};
const ConnectedCookingClass = connect(mapStateToProps)(CookingClass);
export default ConnectedCookingClass;
Then use this component elsewhere like so:
// OtherComponent.js
import ConnectedCookingClass from './ConnectedCookingClass';
const OtherComponent = React.createElement({
render() {
return (
<div>
<ConnectedCookingClass />
</div>
);
}
});
The react-redux bindings will do some smart things for you, like only re-rendering the component when the props returned by mapStateToProps are actually different than their previous value (via a shallowEqual comparison), so you should try to only return values here, no functions. Functions should be returned in mapDispatchToProps.
The default implementation of shouldComponentUpdate in react-redux will return true when:
ALWAYS if the component is a "pure" component (aka stateless-function)
When the props have been updated manually (after componentWillReceiveProps called)
When the store has changed and the new props are different than the old props.
Here's what that looks like from the source code:
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}
The real DOM Rendering is completely handled by React with very efficient innerHTML inserts and only for changes in the new data structure of your application VirtualDomTree.
shouldComponentUpdate() controls if the component should be recalculated or not. You should use it, when you are rendering statical data, for example. The output of the component will not change, so you could just return false and the first version of the component will be used for ever ;)

React reusable stateful component

Let's say I created a component which can be turned on/off based on state.
var onOff = React.createElement(<OnOff />, mountElement);
onOff.setState({ on: false });
Later I'm creating a new component called Parent, which will use OnOff inside it.
render() { return <div><OnOff /></div> }
Now how can I change the OnOff state? There is no way I can call setState on it. And I should not according to React doc. So I have to add initial state to OnOff's props:
constructor(props) {
super(props);
this.state = { on: props.initialOn };
}
then in Parent's render method, set the initialOn prop with its state:
render() { return <div><OnOff initialOn={false} /></div> }
But it's still not working, because whenever I change Parent's state, the OnOff component inside it is not re-created with new initial state. Instead, it is only re-rendered with old state. I have a CodePen to prove it: http://codepen.io/anon/pen/QjMwjO?editors=101
You can update the state of the OnOff component by declaring the update also inside a componentWillReceiveProps function, something like:
componentWillReceiveProps:
function(nextProps) {
this.setState({
on : nextProps.initialOn
});
}
This allows you to update state, when new props arrive. And it is valid react.
You should however consider if you need state in OnOff at all: if the only initial setting and all updates ONLY come from its parent component, then a stateless component would be better.
One of the important things to understand when "Thinking in React" is to figure out which component does State belong to.
Read this in React docs
What Components Should Have State?
Most of your components should simply take some data from props and render it. However, sometimes you
need to respond to user input, a server request or the passage of
time. For this you use state.
Try to keep as many of your components as possible stateless. By doing
this you'll isolate the state to its most logical place and minimize
redundancy, making it easier to reason about your application.
A common pattern is to create several stateless components that just
render data, and have a stateful component above them in the hierarchy
that passes its state to its children via props. The stateful
component encapsulates all of the interaction logic, while the
stateless components take care of rendering data in a declarative way.
Thus, your OnOff should not have state but use properties passed down from the parent instead. I have illustrated this at http://codepen.io/anon/pen/gaxbGm?editors=101
render() {
writeLog("OnOff render called!")
writeLog("Child: " + this.props.initialOn)
return <span>{this.props.initialOn ? "On" : "Off"}</span>;
}
I would also recommend reading "Thinking in React" to get further clarity.

Resources