How can saving React Child Components inside state value be efficient? - javascript

I'm working on some project where I need to add react child components dynamically. While this task may seem to be easy to complete, I'm concerned about the efficiency of my approach. I surfed the internet and found several solutions that have the same design pattern as mine. Still, I'm doubting myself of efficiency.
Below is my example:
class Container extends Component{
constructor(props){
super(props)
this.state = {
children: []
}
this.addChild = this.addChild.bind(this)
}
addChild(){
this.setState({
children: [...this.state.children,
<div>
<h1> My number is {this.state.children.length} </h1>
</div>]
})
}
render() {
return
<div>
<button onClick={this.addChild}> Add child </button>
{this.state.children.map( child => child)}
</div>
}
}
This example is quite simple. But when you have a complex structure, where components have other components that change state, have events listeners and deep structure, Component's state value will contain a huge amount of code and this component will re-render everything on each addition. On 100th addition, it will make 99 unnecessary re-renders.
Of course, I can use Vanilla JS or JQuery to append new components, but I do not find it to be smart decision. I'd like to keep consistency and solve the problem with React tools.
What are your thoughts? What patterns would you suggest?

Ok, I've just found the answer for this question myself. While React invokes render function on each state change, it does not re-render unnecessary components.

3 years after, I see that this approach is terrible. One should store state values in the array and iterate them in the render function. Not only the code will look more clear, but memory usage will decrease as well.

Related

How does the splice work in Function Component(hooks) in React? [duplicate]

I understand that React tutorials and documentation warn in no uncertain terms that state should not be directly mutated and that everything should go through setState.
I would like to understand why, exactly, I can't just directly change state and then (in the same function) call this.setState({}) just to trigger the render.
E.g.: The below code seems to work just fine:
const React = require('react');
const App = React.createClass({
getInitialState: function() {
return {
some: {
rather: {
deeply: {
embedded: {
stuff: 1,
},
},
},
},
},
};
updateCounter: function () {
this.state.some.rather.deeply.embedded.stuff++;
this.setState({}); // just to trigger the render ...
},
render: function() {
return (
<div>
Counter value: {this.state.some.rather.deeply.embedded.stuff}
<br></br>
<button onClick={this.updateCounter}>Increment</button>
</div>
);
},
});
export default App;
I am all for following conventions but I would like to enhance my further understanding of how ReactJS actually works and what can go wrong or is it sub-optimal with the above code.
The notes under the this.setState documentation basically identify two gotchas:
That if you mutate state directly and then subsequently call this.setState this may replace (overwrite?) the mutation you made. I don't see how this can happen in the above code.
That setState may mutate this.state effectively in an asynchronous / deferred way and so when accessing this.state right after calling this.setState you are not guaranteed to access the final mutated state. I get that, by this is not an issue if this.setState is the last call of the update function.
This answer is to provide enough information to not change/mutate the state directly in React.
React follows Unidirectional Data Flow. Meaning, the data flow inside react should and will be expected to be in a circular path.
React's Data flow without flux
To make React work like this, developers made React similar to functional programming. The rule of thumb of functional programming is immutability. Let me explain it loud and clear.
How does the unidirectional flow works?
states are a data store which contains the data of a component.
The view of a component renders based on the state.
When the view needs to change something on the screen, that value should be supplied from the store.
To make this happen, React provides setState() function which takes in an object of new states and does a compare and merge(similar to object.assign()) over the previous state and adds the new state to the state data store.
Whenever the data in the state store changes, react will trigger an re-render with the new state which the view consumes and shows it on the screen.
This cycle will continue throughout the component's lifetime.
If you see the above steps, it clearly shows a lot of things are happening behind when you change the state. So, when you mutate the state directly and call setState() with an empty object. The previous state will be polluted with your mutation. Due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now. This will disrupt all the React's Lifecycle Methods.
As a result, your app will behave abnormal or even crash. Most of the times, it won't affect your app because all the apps which we use for testing this are pretty small.
And another downside of mutation of Objects and Arrays in JavaScript is, when you assign an object or an array, you're just making a reference of that object or that array. When you mutate them, all the reference to that object or that array will be affected. React handles this in a intelligent way in the background and simply give us an API to make it work.
Most common errors done when handling states in React
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// bad approach
const b = this.state.a.push(6)
this.setState({
a: b
})
In the above example, this.state.a.push(6) will mutate the state directly. Assigning it to another variable and calling setState is same as what's shown below. As we mutated the state anyway, there's no point assigning it to another variable and calling setState with that variable.
// same as
this.state.a.push(6)
this.setState({})
Many people do this. This is so wrong. This breaks the beauty of React and is bad programming practice.
So, what's the best way to handle states in React? Let me explain.
When you need to change 'something' in the existing state, first get a copy of that 'something' from the current state.
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// create a copy of this.state.a
// you can use ES6's destructuring or loadash's _.clone()
const currentStateCopy = [...this.state.a]
Now, mutating currentStateCopy won't mutate the original state. Do operations over currentStateCopy and set it as the new state using setState().
currentStateCopy.push(6)
this.setState({
a: currentStateCopy
})
This is beautiful, right?
By doing this, all the references of this.state.a won't get affected until we use setState. This gives you control over your code and this'll help you write elegant test and make you confident about the performance of the code in production.
To answer your question,
Why can't I directly modify a component's state?
Well, you can. But, you need to face the following consequences.
When you scale, you'll be writing unmanageable code.
You'll lose control of state across components.
Instead of using React, you'll be writing custom codes over React.
Immutability is not a necessity because JavaScript is single threaded, but it's a good to follow practices which will help you in the long run.
PS. I've written about 10000 lines of mutable React JS code. If it breaks now, I don't know where to look into because all the values are mutated somewhere. When I realized this, I started writing immutable code. Trust me! That's the best thing you can do it to a product or an app.
The React docs for setState have this to say:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
Basically, if you modify this.state directly, you create a situation where those modifications might get overwritten.
Related to your extended questions 1) and 2), setState() is not immediate. It queues a state transition based on what it thinks is going on which may not include the direct changes to this.state. Since it's queued rather than applied immediately, it's entirely possible that something is modified in between such that your direct changes get overwritten.
If nothing else, you might be better off just considering that not directly modifying this.state can be seen as good practice. You may know personally that your code interacts with React in such a way that these over-writes or other issues can't happen but you're creating a situation where other developers or future updates can suddenly find themselves with weird or subtle issues.
the simplest answer to "
Why can't I directly modify a component's state:
is all about Updating phase.
when we update the state of a component all it's children are going to be rendered as well. or our entire component tree rendered.
but when i say our entire component tree is rendered that doesn’t mean that the entire DOM is updated.
when a component is rendered we basically get a react element, so that is updating our virtual dom.
React will then look at the virtual DOM, it also has a copy of the old virtual DOM, that is why we shouldn’t update the state directly, so we can have two different object references in memory, we have the old virtual DOM as well as the new virtual DOM.
then react will figure out what is changed and based on that it will update the real DOM accordingly .
hope it helps.
It surprises me that non of the current answers talk about pure/memo components (React.PureComponent or React.memo). These components only re-render when a change in one of the props is detected.
Say you mutate state directly and pass, not the value, but the over coupling object to the component below. This object still has the same reference as the previous object, meaning that pure/memo components won't re-render, even though you mutated one of the properties.
Since you don't always know what type of component you are working with when importing them from libraries, this is yet another reason to stick to the non-mutating rule.
Here is an example of this behaviour in action (using R.evolve to simplify creating a copy and updating nested content):
class App extends React.Component {
state = { some: { rather: { deeply: { nested: { stuff: 1 } } } } };
mutatingIncrement = () => {
this.state.some.rather.deeply.nested.stuff++;
this.setState({});
}
nonMutatingIncrement = () => {
this.setState(R.evolve(
{ some: { rather: { deeply: { nested: { stuff: n => n + 1 } } } } }
));
}
render() {
return (
<div>
Normal Component: <CounterDisplay {...this.state} />
<br />
Pure Component: <PureCounterDisplay {...this.state} />
<br />
<button onClick={this.mutatingIncrement}>mutating increment</button>
<button onClick={this.nonMutatingIncrement}>non-mutating increment</button>
</div>
);
}
}
const CounterDisplay = (props) => (
<React.Fragment>
Counter value: {props.some.rather.deeply.nested.stuff}
</React.Fragment>
);
const PureCounterDisplay = React.memo(CounterDisplay);
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/ramda#0/dist/ramda.min.js"></script>
<div id="root"></div>
To avoid every time to create a copy of this.state.element you can use update with $set or $push or many others from immutability-helper
e.g.:
import update from 'immutability-helper';
const newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
setState trigger re rendering of the components.when we want to update state again and again we must need to setState otherwise it doesn't work correctly.
My current understanding is based on this and this answers:
IF you do not use shouldComponentUpdate or any other lifecycle methods (like componentWillReceiveProps, componentWillUpdate, and componentDidUpdate) where you compare the old and new props/state
THEN
It is fine to mutate state and then call setState(), otherwise it is not fine.

Is the reason props in React shouldn't be changed/mutated is because React wants elements to be as predictable as possible?

From React documentation.
Conceptually, components are like JavaScript functions. They accept
arbitrary inputs (called “props”) and return React elements describing
what should appear on the screen.
Considering:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
or
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Will give us the ability to do this:
<Welcome name="Luke" />;
<Welcome name="Leia" />;
to use as we wish in the DOM,
Hello, Luke
Hello, Leia
Now when people prescribe props shouldn't be changed, it would make sense the reason is in my thinking would be like the same as changing the values of attributes of an image tag?
HTML:
<img id="Executor" alt="Picture of Executor" src="/somepath/vaders-star-destroyer-executor.jpg"/>
JS:
Meanwhile in a Javascript file a long time ago in a galaxy far, far away...
var imageOfVadersStarDestroyer = document.getElementById('Executor');
imageOfVadersStarDestroyer.src = "/somepath/vaders-star-destroyer-avenger.jpg"
Because if we keeping changing an elements attribute values this can cause confusion and slower renderings?
So is the reason why the prescription is to never change props in React is because is the library is trying to make elements as predictable as possible?
Setting props outside of React is dangerous and should be avoided. Why? The main reason is that it doesn't trigger re-renders. Hence bugs and unexpected behaviour.
Re-rendering
Most of the time, props are data that is store as state in the parent component, which is manipulated by calling setState() (or the second function returned by React.useState()). Once setState() is called, React re-renders and computes what has changed under the hood, with the latest props and state. Manually assigning values to props, therefore won't notify React that the data has changed and something has to be re-rendered.
The good practice
Making props read-only allows React components to be as pure as possible, which is obviously a good practice anyway even when writing plain JS. Data won't be changed unexpectedly and can only be done so by calling setState() (You might have heard of the single source of truth, which is what React is trying to leverage).
Imagine you notice something went wrong in the app and the data shown to the end user is completely different from the source, it would be a pain trying to find out where the data has been manipulated wouldn't it? :)
never change props in React
means that you should never do this.props.name = "userName" because of React's one way data binding, props are read only, to update a component's props, you should pass a function from the parent that will do that ( in the parent ) , or dispatch an action if you're using redux, a change in the props will trigger a re-render
props is a constant in this case. You will always need it in your components.
But there is a cleaner way to write it or even omit it.
Regular way with Function Expression (same as your exemple)
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
ES6 Object Destructing - explicit
function Welcome(props) {
const {name} = pros
return <h1>Hello, {name}</h1>;
}
ES6 Object Destructing - inplicit, cleaner way
function Welcome({name}) {
return <h1>Hello, {name}</h1>;
}
And of course, you can use the class way which requires the usage of this.props.yourAttr
However, in the new version 3 of create-react-app, changed class components to functional components. You can see this exact modification on Github here.
You can need to learn more about destructing assignment in the old and good MDN linked here or an in-depth approach both array and object destructuring here.

React State management

I am very new to React. On going through the React tutorial, I understand the need to lift the state to the parent component which ensures that the children components can stay in sync and can feed off the major chunk of data residing in the state of the parent. But then , with changing data, won't the setState in the parent fire off the re-rendering of all the children components, decreasing UI performance ? Without using flux or redux, which is the best way to position the state in a react application ?
When you change a component's state, it triggers a re-render for that component.
The re-render will create a new set of virtual elements (returned from the render function), which React uses to represent the new state of the DOM. However, unless there are differences between the virtual DOM and the real DOM, nothing will be changed.
The fact that React's virtual elements are simply lightweight object representations of actual HTML elements makes it fast enough that in most cases, you don't even need to think about the performance cost of calling render for child components.
If, however, you do find yourself with a child component that has a particularly expensive render function—you can prevent it from always re-rendering by implementing shouldComponentUpdate. This allows you to specify with fine grained control, which changes in props or state will actually trigger a component to update.
The other approach is to build a custom state management solution which explicitly ties your components together.
let state = { count: 0 };
let listenForState = null;
function ComponentA() {
let onClick = () => {
state.count += 1;
if (listenForState) listenForState(state);
};
return <button onClick={onClick}>+</button>;
}
class ComponentB extends React.Component {
constructor() {
super();
this.state = state;
}
componentWillMount() {
listenForState(state => this.setState(state));
}
render() {
return <span>{this.state.count}</span>;
}
}
However this quite quickly gets out of hand, and there aren't many situations where it would be better than just lifting state up to a shared parent.
If you find that your state driven child components are nested too deeply in the stateful parent, then it's time to look at Redux instead.
Yes, your mostly right. But you dont need to pass the state of the component to the children, you r passing only props, its not actually the same, because changing of props doesnt need to rerender your component.
There is amazing method in the component lifecycle, called shouldComponentUpdate, default its return true if only shallow equality of states objects are false (if refs are different - thats why mutate of state will not rerender component). And you can also implement this method by yourself, comparing your props to check if you need an update or not. There is a scheme how would it work.
And also, react will not rerender element if the virtual DOM was not changed. So this way you can rerender component if only his props is changed, but if you need to change props of its children - you must rerender current.
Basicly, because of these there is an advice to pull the state of application from top to the leafs component (which returns most of html). You can read about optimization more here.
And must to say :
Your code must become a spaghetti
If you will have a lot of components, which have a lot of events, based on state, so if you need some complex architecture, use Redux or some other global state manager.
If you are looking only for a State Management library, check Duix (https://www.npmjs.com/package/duix).
Redux solves a lot of problems (adding complexity). If you only want to improve the State Management, just take a look at Duix.
PS.: I created that library

Trying to understand data passing in React.js

I want to understand what is being passed from the parent component to the child component properties in the following React.js example (I'm working through this tic-tac-toe tutorial):
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => this.props.onClick()}>
{this.props.value}
</button>
);
}
}
Then there is a board component that does this to render each square:
renderSquare(i) {
return <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} />;
}
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
squares is an array saved in the state of the board.
The render function of the board is like this:
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
handleClick updates the squares array that is in Board without seemingly touching Square. This seems a little wonky from my viewpoint of a traditional Java/C++ programmer where I'm looking for something like pass-by-reference vs pass-by-value. The only thing I can guess about what is happening here is that after handleClick is called, the Board's render function is called again and that is how the new square[i] data value is passed to the Square component. So at that point, Square is rendered with a new set of props but the same state it had before. Is that correct?
Even if I'm correct in my understanding, it's what I'm really trying to wrap my head around as a way of thinking about the code I'm going to write. I'm also curious about more details about the control flow of React.js: When should I expect render() for a component to be called again? And does render of a parent call render of all the children it has? I'm curious about if a parent is re-rendered, do all the old child components get destroyed and then a new set is constructed and rendered? If not, how does React.js know which call goes with which Square object?
I'm interested in any advice about how to understand this or articles with good analogies/descriptions about this subject. Even just some jargon about how to describe what is going on here would help me in my own Googling. Thanks, all!
Full example here
Great question! You're asking the right things and I can tell that you're going to have a strong grasp of React within a short time frame. It seems like you could use some help to better understand the component lifecycle and the beauty of one-way data binding. I'll list some resources that you might find help and I'll try my best to answer your specific questions below.
This is a good article on component lifecycles:
http://busypeoples.github.io/post/react-component-lifecycle/
Another great resource/tool is Redux. Not only is it a great tool for debugging but it also helps you learn because it makes it much easier to visualize what is going on behind the scenes.
Redux: https://github.com/reactjs/redux
Questions & Answers:
1) ... curious about more details about the control flow of React.js
When an instance of a component is being created and inserted into the DOM, the following methods are called in this order:
constructor()
componentWillMount()
render()
compnentDidMount()
These methods are called when a component is being re-rendered:
componentWillReceiveProps()
shouldComponentUpdate()
render()
componentDidUpdate()
2) When should I expect render() for a component to be called again?
It's hard to give you a definitive statement as there are a lot of factors that come into play here. Typically, render() gets invoked when you need to load data from a remote endpoint, changes in the values of Props (which could trigger a state change), and changes in state. In other words, when something changes or updates and it requires DOM manipulation, we will likely re-render.
3) Does render of a parent call render of all the children it has?
4) If parent is re-rendered, do all the old child components get destroyed?
3 & 4 here are pretty similar question. When you use setState it is not only the current component that will do a render, but also all nested components. But if a nested component does a setState it will not affect parent components. From my understanding, child components will not get destroyed unless you specify compoentWillUnmount() or by performing clean up operations.
React re-renders a component and its children whenever it is dirty, i.e. its props or state changed.
In your case, by calling setState(), you change the state of your board component and for this reason trigger a rendering of it and all its children.
Note that this does not necessarily mean that all DOM elements are deleted and replaced, as React may reuse them where possible for performance reasons. This process however is abstracted away by the React library for you.

What's the difference between using a stateless functional component versus calling a method?

I'm trying to understand stateless components and what the difference is between these examples:
class App {
render() {
return (
<div>
{this.renderAFunction('hello')}
</div>
);
}
renderAFunction(text) {
return (
<p>{text}</p>
);
}
}
and this:
class App {
render() {
return(
<div>
<RenderAFunction text='hello'/>
</div>
);
}
}
const RenderAFunction = ({text}) => (
<p>{text}</p>
);
Or if there is any difference at all?
Functionally, there is absolutely no difference. Both end up rendering a paragraph element, but there are other aspects to consider. There are three points to make (in my opinion) when examining both methods:
Reusability: You have to understand to separate components when you need to. If renderAFunction is just meant to generate some JSX based on, for example, an API request, then it's fine being in a method. But if you want to reuse it somewhere else, then separate it into it's own component. A huge part of React is component reusability and getting rid of code duplication. Separating the method into it's own component would be imperative to accomplish this.
Purpose: There are reason to use stateless function components and reasons not to. The whole point of stateless functional components is to not have state and be presentational. If you need to do something that involves the React lifecycle or internal state, keep it as a method, or new class depending on if you want it reusable.
Performance: Using a stateless functional component would be less efficient. This is because it's a component, not just some JSX returned from a method. As of right now, the React team plans on making some optimizations for stateless functional components because they do not contain state and are merely presentational, but this probably won't happen until after React Fiber is done and thus your stateless functional component has no optimizations versus a regular full-fledged class component. That makes it incredibly inefficient versus a method returning some JSX, especially if it's just used once in another component.
A good rule of thumb is to ask yourself, do I need it anywhere else? If not, then keep it in a method. If you don't need it anywhere else, separating the JSX into a separate component would have worse performance and wouldn't follow React's core principles.
If you are going to need it somewhere else, then separate the component so you follow React's concept of reusability.
Your App's render function will be translated into following JS code for your first example:
render() {
return React.createElement(
'div',
null,
this.renderAFunction('hello')
);
}
And the following one for the second one:
render() {
return React.createElement(
'div',
null,
React.createElement(RenderAFunction, { text: 'hello' })
);
}
While they both looks almost the same, there is one significant difference: laziness. React will execute RenderAFunction body only in case it gonna be mounted to the DOM hierarchy.
It is insignificant is your case, but imaging following example:
const LazyApp = () => {
const heavy = <div><HeavyStuff /></div>
// ...
return <div>done it</div>
}
const HardWorkingApp = () => {
const heavy = <div>{HeavyStuff()}</div>
// ...
return <div>done it</div>
}
const HeavyStuff = () => {
// ... do heavy rendering here
}
Lazy app will not perform any heavy stuff at all. Maybe it is not very clear from that synthetic example why one would do anything like that at all. But there are real-world examples out there, when something conceptually similar (creating Components without rendering them) is happening.
Basically, both will serve the same purpose. But the real advantage of creating a component is its reusability.
If you have to render that particular piece of jsx just for this component. Then, there is no need to create a separate component.

Categories