Refresh only the component that changed from the parent - reactjs

I have a react component that is a list, let's call it List, it has a state named items that is an array of object of the form :
{ id: String, name: String }
its render function is something like
render () {
return (
<ul>
{ items.map((item) => {
return (
<Item key={item.id} item={item} />
);
})}
</ul>
);
}
with Item being a react component too, of course.
So now, my question: let's say that at one point, I know that the data changed (it can only be a name change, of course), how can I trigger Item to refresh based on its key from the parent?
I found two solutions so far :
refreshing the whole state, but I don't like it cause it seems overkill.
maintaining an array of references to the child components, but I don't like it cause it feels like a waste of memory.
so what's a 'reactier' way of doing this?

If the items array changed then the render of the Parent is called, since the keys Item components are same, the component is not re-created but its componentWillReceiveProps function is called and the Item component is re-rendered.
What you can do as an optimization is to create a Item component as a Pure component which you can do by extending React.PureComponent. React.PureComponent’s shouldComponentUpdate() only shallowly compares the objects so if the Props haven't changed a rerender on child is not called.
According to the DOCS:
If your React component’s render() function renders the same result
given the same props and state, you can use React.PureComponent for a
performance boost in some cases.
React.PureComponent’s shouldComponentUpdate() only shallowly
compares the objects. If these contain complex data structures, it may
produce false-negatives for deeper differences. Only extend
PureComponent when you expect to have simple props and state, or use
forceUpdate() when you know deep data structures have changed. Or,
consider using immutable objects to facilitate fast comparisons of
nested data.
Furthermore, React.PureComponent’s shouldComponentUpdate() skips
prop updates for the whole component subtree. Make sure all the
children components are also “pure”.

By implementing shouldComponentUpdate(nextProps, newState) in your React components you can calculate whether or not the component needs to update. So if you send a whole new object in, this should trigger already in its default implementation (they don't do a deep-compare), but if the property input.name is changed then it will not detect this. So you could implement this yourself like this:
shouldComponentUpdate(nextProps, newState) {
return this.props.name === nextProps.name; // use === to compare types too!
}
Now I'm a bit rusty on the JavaScript (I prefer TypeScript), so I am not sure if this is the correct way to compare strings.

Using the shouldComponentUpdate() method, you can specify whether or not a component should update based on the current props and the new props which are provided as:
shouldComponentUpdate(nextProps){
return this.props.item.name !== nextProps.item.name;
}
Here's a JsFiddle with a demo: https://jsfiddle.net/Vorcan/Lakj6qwb/
Usually, you have to create a new object instance using something like Object.assign().
Other than the docs, another good resource to check out shouldComponentUpdate is https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/using_should_component_update.html

Related

React will not create a new nested component as expected

In my application I have a page change mechanism that uses one of the properties of parent component state.
So I do something like:
class mainComponent{
state={
mypage:null
}
onclickhandler(page){
this.setState({mypage:page});
}
render(){
return <div>{page}</div>
};
}
In my "page" property I have various input fields and other components.
When I change the page, all inputs get updated with new values. However, my custom nested components are never re-created.
Instead, reach just calls componentDidUpdate on them.
Is there a way to force it to construct a new nested component somehow?
React won't recreate component when it's not needed - it's a part of optimizations and general way react works.
When 'new', 'expected' component exists (the same type, the same or no key id) it only receives updated props.
Updating props sometimes doesn't force rerendering (it's guaranteed for state updates). PureComponent compares props shallowly, you can check shouldComponentUpdate (should return true to force render).

How should I handle component state following single responsibility pattern

I'm new to ReactJs and trying to follow best practices. From my research, I've come across a couple of contradicting articles discussing how implementation should be.
Should state rely on the properties being passed down from a parent component? In the comparisons below, they are both following SRP, but not sure which is best. Would like your advice, Thanks!
1. -- Best Practices for Component State in React.js
First, and probably the most important of all, the state of a component should not depend on the props passed in. (see below for example of what we should not do)
class UserWidget extends React.Component {
// ...
// BAD: set this.state.fullName with values received through props
constructor (props) {
this.state = {
fullName: `${props.firstName} ${props.lastName}`
};
}
// ...
}
2. -- 7 architectural attributes of a reliable React component
Let's refactor to have one responsibility: render form fields and attach event handlers. It shouldn't know how to use storage directly.....The component receives the stored input value from a prop initialValue, and saves the input value using a prop function saveValue(newValue). These props are provided by withPersistence() HOC using props proxy technique.
class PersistentForm extends Component {
constructor(props) {
super(props);
this.state = { inputValue: props.initialValue };
}
// ...
}
3. -- In my case, I have something like the following (wondering if this is an acceptable implementation?) - Should state be handled in Tasks, or in another TasksWithPersistence type of component that sits between TasksWithData and Tasks?
export default function TasksWithData(TasksComponent) {
return class withData extends React.Component {
render() {
const tasks = TaskAPI.getTasks();
return (
<TasksComponent
tasks={tasks}
{...this.props}
/>
)
}
}
}
export default class Tasks extends React.Component {
state = {
tasks: [],
addItemInput: null
};
// ...
componentDidMount() {
this.updateComponentState({tasks: this.props.tasks});
}
componentDidUpdate() {
this.prepUIForNextAddition();
}
// ...
}
The gist of your question seems to revolve around the anti-pattern that is to take some props and duplicate it into the state. This, mutating of props, isn't the purpose of the state. Props are immutable, duping them to the state defeats this design.
The purpose of the state is to manage things that are specific to the React Component, i.e. tightly scoped to only that React component. For instance a showHide switch for something to display within the React component. Think of the state as a locally scoped variable if it helps.
Most of the time this anti-pattern of duping the props can be satisfied by a function within the React object. For example, your state.full_name variable becomes a named function, fullName, bound to the React Component. (all code examples are assuming JSX syntax)
Note: in JavaScript camelcase is the naming structure for functions and variables, I'm assuming you're coming from ruby based on the underscore naming convention. IMO it's best to stick to the convention of the language with which you're writing the code. This is why I use camelcased naming.
...
fullName() {
return this.props.firstName + " " + this.props.lastName
}
...
That function can then be called within the render of the component
# in render() portion of your React component, assuming jsx syntax
<p>Hello, {this.fullName()}</p>
Note: Remember that in ES6 you have to bind the methods in your react class in the constructor or use => syntax so that you can call them with this.
...
constructor(props) {
super(props);
this.fullName = this.fullName.bind(this);
}
...
You could also decompose the relevant parts to a new Component called FullName if it will be utilized by multiple components.
<FullName firstName={this.props.firstName} lastName={this.props.lastName} />
Technically, "the react way" is, at least in this author's opinion, to decompose this into another component for reusability. However component reuse needs to be weighed against the complexity added, i.e. don't optimize prematurely. So you may not want to take that too far at first. The times when it's necessary will emerge naturally.
A very broad generalization of React's props is that they are guaranteed, are immutable, and they flow down like a waterfall from the topmost component. If you need to update them, update them at the highest level where it makes sense.
In a soley React based approach, if you have something that a parent needs to be aware of, "lift" that part of the code up to the parent and vice versa bind it down to the child as a props, e.g. an AJAX function that calls an API. I think of it as trying to keep the components as dumb as possible.
The parent becomes the "source of truth" for the item you "lifted". The parent handles the updates, and then passes the results to the children. So in the parent, it may exist as a state variable and then get passed as props to the child object, which then passes it along as props to it's child object, etc. The children would update as the state gets changed in their parent when it propagates down through the chain as props.
If your app is React only, i.e. no stores that manage objects such as in the flux pattern or redux pattern, you may have to store things in the topmost objet's state which technically could be viewed as bad. As your system becomes more complex, this functionality would be better handled by flux or redux's parts.
Hope this helps!
There is a huge difference between example 1 & 2.
In example #1, the reason it's bad to set state from the those props in that way is that if the props change, the widget will not update. Best practices or not, that is just wrong and bad in any framework. In that particular case, there really is no point in even using the state. Props alone will suffice.
In example #2 the prop is only being used to give the state an initial value (The prop is even named initialValue), implying that further changes to the state will be controlled by the component regardless of prop changes. It does not break single responsibility principle to use props for an initial state, especially when it's explicitly use for that purpose.
I really don't see those two examples as being contradictory because they are completely different. Further, there is no rule in single responsibility principle that you can't set state from props, you just need to pay attention to the context in which you are doing it.

React setState on array of objects rerenders every component

I'm using React, and want to update array of objects in a state. The overall answer in internet was to use a similar approach to below:
const newState = this.state.elements.slice().concat({key: 'val'});
this.setState({elements: newState});
But the problem I encountered with this issue is that when these data are binded into a render function and components, it re-renders every component.
Example include (map is used from lodash to retrieve index while mapping):
render() {
return (
<div>
{map(this.state.elements, (el, index) => <Component key={`el-${index}`} el={el} />)}
</div>
);
}
Even though, the array order doesn't change and the key's of the components doesnt change, everytime the state changes, it re-renders and mounts the component from scratch.
Is there a possible best practice solution to this issue?
Best,
Y
Whats happening is that when you call setState the React Virtual Dom diffs state and calls render again. Render calls to a Map function which renders each component. Each component is thrown away and redrawn because the parent component changes.
This is normal and expected behavior by React. It's doing what you're asking of it, you changed state, it redraws the components.
But your question is how do I append to the list of components my new component without redrawing?
I think the issue is that you're relying on the unique key of react to be the index of the array item.
<Component key={`el-${index}`}
That will forever change every time you update state. What you need to do is use something like name, or string or a generated key or something from the data.
You are using just map function index value and of course after adding new element to the list will trigger a re-render because key value for each of them was changed.
if you did something like this:
<Component key={`el-${item.id}`}
Where item id is a constant but unique value, like a primary key in a database table. It would not redraw. The key should have a deterministic value.
See React Docs and this other post for more details.

PureComponent's shallowCompare is not working as expected

I am trying to avoid unnecessary re-rendering of a child component (Review) by extending it from PureComponenet.
This component is being called from the parent component (Reviews), which is not a PureComponent.
By debugging I am seeing that whenever a change occurs to one of the review, all the reviews related to that product are getting re-rendered. Obviously Reviews' props gets updated and it passes individual Review's relevant props to individual Review components.
where I am also seeing this,
nextProps.reviewer === this.props.reviewer_info -> false
nextProps.reviewer_info = []
this.props.reviewer_info = []
I believe that pureComponent uses shallowCompare and it should return true while comparing 2 empty arrays, is that correct?
Does my parent component also needs to be a PureComponent in order to stop unnecessary rendering of my child components? Is that the missing part?
My review component has other child components, which I know also should be declared pureComponents. (I will update them but does this stop Review component from achieving pureComponent's benefits?)
Or above 2 are not the correct reasons for the re-rendering in-spite of using pureComponent and I am missing some other part?
Does my parent component also needs to be a PureComponent in order to stop unnecessary rendering of my child components?
No, that's not necessary.
I believe that pureComponent uses shallowCompare and it should return true while comparing 2 empty arrays, is that correct?
It is not. The shallow comparison uses === to compare the old and new value of each property on props and state. But as you can see in a browser console [] === [] is false. This is because each [] expression creates a new array object that is unequal to the others.
As a consequence, a simple array or object literal in a property (e.g. <Review prop={[]}/>) will trigger a rerender of Review whenever Reviews is rendered, negating the effect of the PureComponent.
To enjoy the optimized behavior, you will need to make sure that each non-primitive prop keeps referring to the same object instance as long as its value doesn't change (e.g. by keeping it in Review's state). Furthermore, if there is a change, you will need to create a fresh top-level object for that prop, rather than modify the existing one in-place (so don't use push or assign directly to the prop objects properties.) The immutable-js package can be helpful with this, but es6 spread operators often work fine as well.
My review component has other child components, which I know also should be declared pureComponents. (I will update them but does this stop Review component from achieving pureComponent's benefits?)
It shouldn't affect Review (unless you create the children in the render method of Reviews, like <Review ..>..<Child ../>..</Review>, in which case props.children will change every time and trigger rerendering.)
You can use shouldComponentUpdate() alternatively if your prop/state is an array:
shouldComponentUpdate(nextProps, nextState) {
return JSON.stringify(this.props.reviewer_info) !== JSON.stringify(nextProps.reviewer_info);
}

Selective children component render

A basic question I need help here.
Whenever this.setState invoked at parent components, all the children components will be rendered. This will cause the performance issue if I have huge amount of child components.
Lets give an example,
Parent Component
handleToggleTick() {
const newObj = Object.assign({}, this.state, { iconName: ''});
this.setState({
iconName: newObj.iconName,
});
}
render() {
return(
<ChildComponentA iconName={this.state.iconName} toggleTick={() => this.handleToggleTick}></ChildComponentA>
<ChildComponentB></ChildComponentA>
<ChildComponentC></ChildComponentA>
)
}
Based on the example above, whenever handleToggleTick invoked from childcomponentA, setState invoked for new iconName. What I want is, only ChildComponentA only the one get render since props.iconName is related to it, but not for childcomponentB and childcomponentC.
I understand there is an option to check shouldComponentUpdate in childcomponent to prevent it get render. But, imagine I have over 100 of childcomponent, would it be frustrating to write over 100 times of shouldComponentUpdate method?
I need help here, please advice!
React doesn't provide any way to render children selectively. The component will either render or not. But I need to highlight a few points why this is not a problem when we use React in practice.
First of all, you don't need to manually implement shouldComponentUpdate for each component. If you don't want to rerender component if its props and state haven't changed, you can just extend from the PureComponent class instead of the Component class. Note that React.PureComponent's shouldComponentUpdate() only uses shallow comparison for state and props. But this shouldn't be a problem if you follow react best practices and avoid mutating the state.
Also, it's not practical to have more than 100 different components in one render method. React always encourages decomposing your UI into smaller components and using component composition. When we follow this approach, components will be nested inside each other in different levels instead of having a large number of components in one render method.
What I'm trying to explain is it's more practical and easy to manage when we compose our component in a nested fashion (2) rather than having lots of components inside a big container component (1).
In your example, if ChildComponentB and ChildComponentC are inside another component called ChildConatainerComponent then we only need to implement shouldComponentUpdate() for ChildConatainerComponent. Then it will automatically stop rendering any child element inside it.
render() {
return(
<ChildComponentA iconName={this.state.iconName}
toggleTick={() => this.handleToggleTick}/>
<ChildConatainerComponent/>
)
}
class ChildConatainerComponent extends PureComponent {
render() {
return (
<div>
<ChildComponentB/>
<ChildComponentC/>
</div>
);
}
}
Another very import concept to keep in mind is calling render function doesn't mean that React recreates all the DOM elements again. The render method only make changes to React virtual DOM which is an in-memory representation of DOM and it's faster than actual DOM. Then React compare versions of virtual DOM which are before the update and after the update and the actual DOM will be updated with only what has actually changed.
Another solution you could consider is moving iconName into ChildComponentA, considering this is the only component related to it.

Resources