When does React create a new component instance? - reactjs

I have a question about the constructor in React
Look at a example at https://codesandbox.io/s/lpr147kmyl
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class SimpleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
number: props.number,
}
console.log('constructor', this.state.number)
}
render() {
return (
<div>
{this.props.number}
</div>
)
}
}
class App extends React.Component {
constructor(props){
super(props)
this.state = {
showFirstComponent: true,
}
setInterval(() => (
this.setState(previousState => (
{ showFirstComponent: !previousState.showFirstComponent }
))
), 1000);
}
render(){
let c1 = <SimpleComponent number="1" />
let c2 = <SimpleComponent number="2" />
return (
<div className="App">
<h1>
{this.state.showFirstComponent ? c1 : c2}
</h1>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I checked the console log and saw only one log message.
I thought there should be two log messages there as I create two instances of SimpleComponent :
let c1 = <SimpleComponent number="1" />
let c2 = <SimpleComponent number="2" />
My question is:
When does React create new a component instance and calls the component's constructor?
Thanks,

According to the doc, constructor is only called before a component is mounted. If you add two more React Lifecycle functions to your code example: componentDidMount and componentWillUnmount, you'll see that the component only mounted once in the entire process, regardless of the times the state changes. If you then add shouldComponentUpdate to the component, you'll see that there is only one instance of SimpleComponent and the upper-level state change is shown as props change on SimpleComponent.
The reason why you don't see two instances mounting and unmounting here is that variables c1 and c2 are not instances of SimpleComponent as commonly and conveniently assumed. Instead, they are ReactElement, which is a description of the component instance.
The primary type in React is the ReactElement. It has four properties: type, props, key and ref. It has no methods and nothing on the prototype.
React (Virtual) DOM Terminology
Only when the element gets rendered, an instance of the component gets created and constructor and the Lifecycle functions get called at their respective time points.
So then why, as in your example, the element c2 never gets to be used to create a component? This goes back to how React decides when and what to update at a certain time (you can read more about it here:
When a component updates, the instance stays the same, so that state is maintained across renders.
In your example, when the App component renders c2 instead of c1, React sees that the new component element is the same as the old one so it reuses the component(i.e. only a single instance exists) and updates the props/states accordingly.
Here are some more readings that I find useful to understand this problem:
https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/birth_mounting_indepth.html
Mark Amery's answer to this question is also very helpful.

If someone has landed here for react-native.
Assigning different keys for different instances will create new instances instead of reusing the same instance.
<MyComponent
key={this.state.uniqueInstanceKey}
myData={this.state.myData}/>

Your ternary is checking the state for showFirstComponent and rendering only one.
{this.state.showFirstComponent ? c1 : c2}
If you remove that all together, and just do
<SimpleComponent number="1" />
<SimpleComponent number="2" />
You'll get two logs. To further answer the question, constructor is called when the component is mounted, so usually once, unless you're dynamically mounting stuff. To get back at your ternary:
{this.state.showFirstComponent ? c1 : c2}
If you were to toggle this.state.showFirstComponent with a button lets say:
<button onClick={() => this.setState({
showFirstComponent: !this.state.showFirstComponent
})>
Toggle c1/c2
</button>
Every time you hit this button, component will be unmounted, and another one mounted, so you'll get a console log. Here's the order of react lifecycle methods between mount and unmount. However, if you do anything else, eg change the props passed, change the internal component state of <SimpleComponent />, it will NOT unmount, but rather, update, which calls the render method, and not the constructor. I suggest adding a console.log for some lifecycle methods in your <SimpleComponent /> and trying to trigger them, to better understand the component lifecycle. Or read an article about it.

Related

Too many methods in class component in React – problem refactoring

I am still fairly new to Reactjs and I am struggling with one thing.
I have built a small React app that consists of:
App.js at the very top –the only class component that controls all the states
3 stateless components ( no1 , no2, no3 rendered in App.js (all with smaller components inside them, but that’s irrelevant)
App.js state includes 3 arrays all of which are used by those 3 stateless components rendered.
And then, in App.js I have around 400 lines of methods where I am modifying state by calling setState. That’s quite long....
My question is: is there anyway to split this file? To move methods to their respective components: no 1, 2 and 3.
It seems impossible to me as having all the states in one class requires calling setState (having setState methods) in the same class only.
This might be a stupid question but:
Is it possible to modify state outside parent that holds this state (App.js), for instance, modify it in a stateless component no 1, and still keep parent updated about the change so that it can inform stateless components no 2 and no 3 about the change.
What’s the best practice in my case?
thanks
What i do for code/logic splitting is:
Keep the App.js as simple as possible
If there is a lot of logic in a file/Component, i split the file to index.js and Component.js and keep the logic in index.js and the JSX/HTML in the Component.js
In your case you can:
Remove all logic from App.js
Create 3 folders no1, no2 and no3
Inside each folder create two files - index.js and ComponentNo1.js etc
In the index.js create a Component with state and in ComponentNo1.js create a stateless component that returns the JSX/HTML.
This is what I would recommend. If you find it hard to do it, post your App.js logic so that I could help you do it.
Sure something like this is completely valid and is a common React practice.
I've written a sandbox to give you an idea of how this works:
https://codesandbox.io/s/zxl2owp2np
But to explain in detail, let's assume you have the following components:
class App extends React.Component {
state = {
value: 5
};
increaseValue = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<div className="App">
<Example1 value={this.state.value} increaseValue={this.increaseValue} />
<Example2 value={this.state.value} />
<Example3 value={this.state.value} />
</div>
);
}
}
const Example1 = (props) => {
const value = props.value;
return (
<div>
I am Example1. The value is {value}
<button onClick={() => props.increaseValue()}>Click</button>
</div>
);
}
const Example2 = (props) => {
const value = props.value;
return (
<div>
I am Example2. The value is {value}
</div>
);
}
const Example3 = (props) => {
const value = props.value;
return (
<div>
I am Example3. The value is {value}
</div>
);
}
Our main App component has a state value of 5 that we pass into our 3 child components. The child components are all using that value to display data. In the App component, we defined a state-updating function called increaseValue, which we pass in to Example1 as property. Now Example1 can use increaseValue by calling props.increaseValue()
props.increaseValue() is bound to our App component's execution context. So it will update the value in App's component state. Now with the updated value, it gets shared across all our Example components for use.
Regarding your question about moving methods to the components that are using them. This is not a common practice. Typically you define the function inside the component where the state is going to be updated. Meaning, if you have a function that is meant to update the App component, it should be defined in the app component.

Strange problem using conditional rendering of component

I have a simple React component that takes in a single prop, if this prop is 0 show one component otherwise show another.
On page load it equals 0 so the first component loads, once my Redux store is updated from localstorage this value in the parent component updates its local state passing down the new prop to my component.
What happens is rather than switching to render my other component it puts the second component inside of the first... I have never seen this behaviour before and cannot for the life of me figure out why.
I have tried all sorts, switching between stateless and functional components, I have tried using componentWillReceiveProps to receive the updated prop and setState based on this and have my conditional logic based on state. All with the same results.
const ComponentThatGetsUpdated = (props) => (
props.PropThatGetsUpdated === 0 ? <ComponentOne /> : <ComponentTwo />
);
I expect on page load the output to be the contents of
<div>contents of component one</div>
then once my redux store has updated the new props are passed down the output should now be the contents of component two
<div>contents of component two</div>
The actual output once props have been updated is
<ComponentOne />
<ComponentTwo></ComponentTwo>
<ComponentOne />
FYI the parent component of ComponentThatGetsUpdated looks like the below:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {PropToPassDown: 0}
this.sub = this.sub.bind(this);
const sub = store.subscribe(this.sub);
}
sub() {
//when my redux store is updated this function runs and sets PropToPassDown to the new value, for this example it sets PropToPassDown in local state to 1
}
render() {
return (
<ComponentThatGetsUpdated PropToPassDown={this.state.PropToPassDown} />
)
}
}

React Context API is slow

I'm experimenting with new Context API and hooks. I've created an app with sidebar (treeview), footer and main content page. I have a context provider
const ContextProvider: FunctionComponent = (props) => {
const [selected, setSelected] = useState(undefined);
const [treeNodes, setTreeNodes] = useState([]);
return (
<MyContext.Provider
value={{
actions: {
setSelected,
setTreeNodes
},
selected,
treeNodes
}}
>
{props.children}
</MyContext.Provider>
);
Im my content component I have a DetailsList (Office Fabric UI) with about 1000 items. When I click on the item in the list I want to update selected item in context. This works but it is really slow. It takes about 0,5-1 seconds to select item in the list. The list is virtualized. I have tried it on production build. Thing are a bit better but there is a noticable lag when clicking on list.
Footer is consuming myContext to display information about selected item.
Here is a bit of code from my component
const cntx = useContext(MyContext);
const onClick = (item) => {
cntx.actions.setSelected(item);
};
Am I using the context wrong?
I've created a sample sandbox to demonstrate.. You can scroll to about 100-th index and click a couple of times to see how it gets unresponsive.
https://codesandbox.io/s/0m4nqxp4m0
Is this a problem with Fabric DetailsList? Does it reRender to many times? I believe the problem is with "complex" DatePicker component but I don't understand why does DetailsList get rerenderd? It's not using any of context properties within a render function. I would expect only Footer component to rerender on every context change
Caveats
Because context uses reference identity to determine when to re-render, there are some gotchas that could trigger unintentional renders in consumers when a provider’s parent re-renders. For example, the code below will re-render all consumers every time the Provider re-renders because a new object is always created for value:
class App extends React.Component {
render() {
return (
<Provider value={{something: 'something'}}>
<Toolbar />
</Provider>
);
}
}
To get around this, lift the value into the parent’s state:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
https://reactjs.org/docs/context.html#caveats
Memorize your selected item with useMemo to avoid creating an new object if you are referring to the same item. Then pass it in a dedicated context
In your solution, whenever your component is rerendered, a new instance of value is passed down as a prop. This will trigger the re-rendering of the children as well.
If you use hooks, to prevent this, memoize the value object that you want to pass, in the useMemo or useCallback hook. In fact, this should be applied as a basic React practice to any of your components, not just a context component. Unless you're passing a primitive value (string, number, ...), don't create an instance or an inline function directly and pass it as a prop. Make sure the props you're passing don't get changed after each rendering cycle of the React component.

Functions and re-renders in React Context

I have two questions about the new React Context-api:
The React docs has the following example Updating context from a nested component. Is there some specific reason the toggleTheme-function is declared in the constructor (and not as a class method)?
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State also contains the updater function so it will
// be passed down into the context provider
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// The entire state is passed to the provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
ReactDOM.render(<App />, document.root);
In lot of the examples the Provider-component has the state of a high-up -component as its value (just like in the above example). This means that every time I want to update the Context-value, I need to update the state of the high-up -component. This means that the high-up -component re-renders as the state is updated, which in turn means that all of its child-components also re-render. But all I wanted was for the Consumer-components listening to that Provider-component to re-render. Now basically the whole app re-renders everytime I update the Context-value...Am I missing something?
toggleTheme is passed as a callback and should be bound to correct this. If it was class prototype method, it would requirethis.toggleTheme = this.toggleTheme.bind(this) in constructor any way. See this related question.
As the documentation states,
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
A component that contains Provider (App) should be re-rendered to provide new value, while its descendants shouldn't. In this example its direct children (Content) could be PureComponent to prevent unnecessary re-renders in entire hierarchy. There won't be significant performance improvements for a context that affects entire application like theme context.

How does one React component call a method in another React component?

My page contains two completely separate React components (different files, different classes, no parent-child relationship).
How can one component call an instance method in another component? The problem seems to be obtaining the instance of the target component.
EDIT: Both components share the same parent (i.e. they are rendered in the same render() method) but I still don't know how to pass the reference of the target component to the calling component.
The short answer is: they don't.
It's not clear what you're trying to accomplish, so I can't speak to the specifics of your case, but the way React components "communicate" with one another is via state and props. For example, consider a Page component that has two child components, CompA and CompB, rendered something like this:
<Page>
<CompA />
<CompB />
</Page>
If CompA needs to pass something to CompB, this is done through state on the Page component, with that state exposed as props on CompA and CompB, something like this:
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
sharedValue: 42,
};
}
onChangeSharedValue(newValue) {
this.setState({ sharedValue: newValue });
}
render() {
return (
<div>
<CompA
sharedValue={this.state.sharedValue}
onChange={this.onChangeSharedValue}
/>
<CompB
sharedValue={this.state.sharedValue}
onChange={this.onChangeSharedValue}
/>
</div>
);
}
}
If CompA needs to change the shared value, it calls the onChange handler, which will change the state on the Page component. That value will then be propagated down to the CompB component.
There is no direct communication between components like you're describing; it is all done via state and props.
"Props down, Events up."
If you provide us a specific example of what you're looking for, I can update this post with a more specific response.
But in general, there are a couple of strategies that you can take. Some of them are presented here.
The preferred approach is to simply move your calling method to the parent component. It's a common strategy in React.
If you're not able to, then the next step would be to write an event handler for the parent, and then pass this event down to the first child component.
Use this event to pass information up to the parent, so that when it gets triggered, data can be passed as props down to the second component.
I only recently started doing React development and I found a solution for this problem that suits me. Admittedly, I haven't seen it referenced anywhere and when I showed it to a colleague who's been doing React for years, he kinda furrowed his brow and felt that it wasn't "right", but he couldn't really articulate to me why it's "wrong". I'm sure I'll be shouted down for it here, but I thought I'd share anyway:
File #1: objects.js
let objects= {};
export default objects;
File #2: firstComponent.js
import React from 'react';
import objects from 'objects';
class FirstComponent extends React.Component {
constructor(props) {
super(props);
objects['FirstComponent'] = this; // store a reference to this component in 'objects'
}
doSomethingInFirstComponent() {
console.log('did something in first component');
}
render() {
return (<div></div>);
}
}
export default FirstComponent;
File #3: secondComponent.js
import React from 'react';
import objects from 'objects';
class SecondComponent extends React.Component {
render() {
objects.FirstComponent.doSomethingInFirstComponent(); // call the method on the component referred to in 'objects'
return (<div></div>);
}
}
export default SecondComponent ;
When SecondComponent renders, it will trigger the console.log() in FirstComponent.doSomethingInFirstComponent(). This assumes, of course, that FirstComponent is actually mounted.
The "React Guys" that I know seem to think this approach is somehow evil. It uses a simple JavaScript object outside the normal React scope to maintain a reference to any existing objects that I choose to store there. Other than them telling me that "this isn't the way you do things in React", I haven't yet found a good explanation for how this will break or otherwise screw-up my app. I use it as a low-grade replacement for massive-overkill state-management tools like Redux. I also use it to avoid having to pass properties down through dozens of layers of React components just so something at the last level can trigger something waaaaay up in the first level.
That's not to say this approach doesn't have it's problems:
It creates an obvious dependency between the generic objects object, any component that is designed to store a reference to itself inside objects, and any component that wishes to utilizes those references. Then again, using any kind of global state-management solution creates a similar dependency.
It's probably a bad solution if you have any doubt that FirstComponent will be mounted before you try to call it from within SecondComponent.
I've found that just having the reference to a React component won't allow you to do all the things that React components can do natively. For example, it won't work to call objects.FirstComponent.setState(). You can call a method in FirstComponent, which in turn can invoke its own setState(), but you can't invoke FirstComponent's setState() directly from within SecondComponent. Quite frankly, I think this is a good thing.
You can, however, directly access the state values from the components referenced in objects.
This should only be done with "global" components (components that functionally serve as singletons). If, for example, you had a simple UI component called BasicSpan that did little more than render a basic span tag, and you proceeded to use that component over and over again throughout your React app, I'm sure it would quickly become an unmanageable nightmare to try to place references to these simple components in the objects object and then try to intelligently manage calls to those components' internal methods.
you can send an event as props and call it from other component.
Say you have a class
Class A{
handleChange(evt)
{
this.setState({
name:evt.target.value
})
}
render{
return(
<div>
<ComponentB name={this.state.name}{ onChange={this.handleChange}/>
</div>
);
}
}
Child Component
Class B{
handleChange()
{
//logic
}
render{
return(
<div>
<input type="text" onChange={this.props.onChange}/>
{this.props.name}
</div>
);
}
Here in Component B when you change the input it will call the method
of class A and update state of A.
Now getting the updated state as props in component B will give you
the changed text that you just entered

Resources