I've been working on a project which is coded with React. I have a component set that I implemented many components for my own requirements. Many of these act like a composite component. For example, TextBox component which has its own label, own error message mechanism and own input filter etc. Moreover, you know, components have props to manage sth.
Everytime to update my component view (render), I use componentWillReceiveProps and I compare the props changes.
But everytime implementing the componentWillReceiveProps method is so repulsive.
Is there any way to pass props from top to down without using componentWillReceiveProps. I don't want to compare props changes manually. Is there any way to do it automatically.
When I change the props in parent, I'd like to update all views just changing the some prop values from top to down.
I'm not an react expert and performance is not my first purpose also!
One more thing that the answer is not use Redux!
I'm waiting your creative approaches and helpful ideas.
Without seeing the code for the particular thing you're working on, I may be missing something about what you're doing...
As others have commented, React will re-render your component if new props are provided, regardless of whether or not you implement componentWillReceiveProps -- the only reason to implement it is to do some kind of specific comparison or set a state based on new prop values.
From the React docs (emphasis mine):
componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
In other words, if you have a component like:
<TextBox title={"Foo"} content={"Bar"} />
That internally passes prop changes on to a couple of child components like:
class TextBox extends React.Component {
render() {
return (
<div className={'text-box'}>
<Title text={this.props.title} />
<Body text={this.props.content} />
</div>
);
}
}
Then each time new props are passed to <TextBox>, <Title> and <Body> will also get re-rendered with their new text props, and there's no reason to use componentWillReceiveProps if you're just looking to update with prop changes. React will automatically see the changes and re-render. And React handles diffing and should fairly efficiently re-render only things that have changed.
However, if you have a separate state value that needs to be set in response to props, for example, if you wanted to show a "changed" state (or whatever) on the component if the new props are different, then you could implement componentWillReceiveProps, like:
class TextBox extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.content !== nextProps.content) {
this.setState({changed: true});
}
}
render() {
const changed = this.state.changed ? 'changed' : 'unchanged';
return (
<div className={`text-box ${changed}`}>
<Title text={this.props.title} />
<Body text={this.props.content} />
</div>
);
}
}
If you're trying to prevent re-render in cases where it's unnecessary for performance, do as Andrey suggests and use shouldComponentUpdate: https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
TLDR; unless you're setting component state from props, there's likely no need to run new props through componentWillReceiveProps
UPDATE Feb 2018: in a future release, React will be deprecating componentWillReceiveProps in favor of the new getDerivedStateFromProps, more info here: https://medium.com/#baphemot/whats-new-in-react-16-3-d2c9b7b6193b
There are few suggestions:
Don't copy props into state in componentWillReceiveProps - just render directly from this.props
If your component need performance tweak (and only if there is problem with performance):
start from using shouldComponentUpdate in generic form like advised here https://facebook.github.io/react/docs/shallow-compare.html
If generic approach doesn't work for you - write custom code
The general approach, how to develop text-box-like components is to keep it stateless.Component renders props directly, and notifies parent component about changes, it don't cares about managing value.
Hope this will help
Please consider pureComponent which by defualt implements the shouldComponentUpdate inside which shallow equals is used for comparison between previous and next
try following codes:
class MyComponent extends PureComponent {...}
Related
My parent component sends a prop called array and if this prop changes, I want the child component to re-render with the new prop.
So here is what I added in my child component:
componentDidUpdate = prevProps => {
if (!isEqual(prevProps.array, this.props.array)) {
this.setState({array: this.props.array});
}
};
It seems to be working, although I'm wondering if I thought too much and there's an easier way. Someone suggested If they're equal, couldn't you update the state with the new array anyway? earlier but I wasn't quite sure what that meant and haven't been able to get clarity. Any suggestions?
In React, the Child component should render in case there is a change in its props, so this code in componentDidUpdate is not necessary as the component will re-render on its own.
And if you also want to update a state then this piece of code is fine in that case. Although it's recommended to not have the props and state with the same name.
Let's say I have a lot of app state to manage in my React application.
Therefore, I would like to split the state into smaller, manageable chunks.
For example I have the following main component with state and methods that alter this state.
class App extends Component {
constructor(props) {
super(props);
this.state = {
foo: ['some', 'items'],
bar: [{ arr: 'of objects'}]
}
}
changeFoo() {some code in here...}
changeBar() {some code in here...}
}
The state and methods written in the App component are getting out of hand. Yet it must be written in the App component since the state is passed to other components as props.
How would you usually manage this?
When you see that the state of your React application is getting out of hand, it's usually time to bring in a state management library like Redux (there're a few and Redux is the most popular one).
It'll help you have a global state that is managed in a reasonable way.
When we see how React works. It is based on one-directional data flow.
So, usually the Application state is kept at the top most Component (Say, App Component) in your case. So that data/state can be passed down as props to the component that needs it.
There, however may be the cases where children components of the parent, needs to work with the same data(Say in case of an event - a button click that happens in the child component.) In that case we write a function in the parent component and pass the function as props to the children, so that the state gets updated in the parent itself and any child gets updated data.
In pure React (without using any state management library), we have to pass the state as props to work with our app. But in case you choose to use a state management library such as Redux, then the components (known as Containers) can directly communicate with the Application State.
And If your application state contains objects within objects(like you have shown) or Array of Objects containing more Objects, then you cannot use setState() to update the state directly. In most of the cases, you take copy of the state and then use JSON.parse(JSON.stringify(state)) to do deep cloning and work with the state in a best possible manner.
There are other things in the example, the functions that you have used within the class , you need to bind the scope of this variable to point to the current class. This we do inside the constructor method, or simple make use of arrow function in order to avoid errors.
If you need more explanation, I will share with you :)
One solution is to make a generic change() function with a parameter for the key that should be changed:
change(key, value) {
setState({key: value, ...this.state});
}
Now when you want to add a listener to a child component:
<Foo onChange={ value => change('foo', value) }/>
<Bar onChange={ value => change('bar', value) }/>
Here's my situation:
shortArray.map(data => (
<div className={this.state.active === data.id ? "show" : "dontShow"}>
{this.processAVeryLongArray()}
</div>
))
I hope you get the idea. Just a state change happens, and processAVeryLongArray is independent from it. The only thing I need the state is to change the classname of an element, but the children doesn't change. By the way, it rerenders all children. How to solve this problem?
The ideal approach is to make the child components Pure Components. If the props to the child components are not changing, the Pure Components will not re-render.
You can use the shouldComponentUpdate(nextProps, nextState) lifecycle hook. You write logic to check the value of your properties and state, after checking you return true to re-render the component, else return false to deny re-rending of the component. Read more about them on their official docs.
or you could use React.PureComponent instead of React.Component, if you want React to handle re-rendering logic by comparing all old and new props and state values behind the curtain.
You can use getDerivedStateFromProps
inside your child component to decide whether each of them should update or not.
Here's also some techniques, best practices and pitfals.
How does a component gets to know, its props are changed so that it can re-render?
Eg:
function component(props){
return <p>{this.props.childVar}</p>
}
function parentComp(){
let parentVar = 0;
setTimeout(()=>{
parentVar++;
//what happens between: after this statement is executed and till the child component is re-rendered ?
}, 1000)
return <component childVar={parentVar} />
}
One of the most important aspects of React's API is that it is declarative, and that you don't have to worry about when and how changes are being captured and handled.
Basically, each time you call render, a different tree of react elements is being made and React’s diffing algorithm will compare the old and the new tree, and will make the changes if necessary.
From a component point of view, the instance itself does not know about trees, changes and updates. After all it's just a function that returns a react element based on its paramters (props) and local state (if there is any).
You can read more about it in the Docs
In your code snippet the child component will never re-render, because what you do is just change a local variable, and child component will never know about that. So, short answer is component doesn't know is props changed.
There is two reasons for component to re-render:
this.setState was called
A parent component re-rendered
componentWillReceiveProps and other lifecycle methods seems like deceptive temptation to bring unnecessary complexity and noise to the code in the hands of inexperienced React coder. Why do they exist? What are their most typical use cases? In the moment of uncertainty, how would I know if the answer lies in the lifecycle methods?
I have been using react for couple of months now, and most of my work is creating a large application from scratch. So the same questions have presented themselves in the start.
The following information is based on learning while development and going through multiple docs out there to get it right.
As asked in the question here are couple of uses cases for the lifecycle methods in react
componentWillMount()
This is called once on the server side, if server side rendering is present, and once the client side.
I personally have used it just to do api calls which do not have direct effect on the components, for example getting oAuth tokens
componentDidMount()
This function is mostly used for calling API's (here is why to call it in componentDidMount and not in componentWillMount)
Components state initialisations which are based on the props passed by parents.
componentWillReceiveProps(nextProps,nextState)
This function is called every time props are received except the first render
Most common use I have encountered is to update the state of my current component which i can not do it in componentWillUpdate.
shouldComponentUpdate(nextProps, nextState)
This method is invoked before the render happens when new props or states are received. Here we can return false if the re-render is not required.
I see this as a performance optimisation tool. In case of frequent re-rendering of parent component this method should be used to avoid unnecessary update to current component
componentWillUpdate(nextProps,nextState)
this function is called every time a component is updated, it is not called when component mounts
Carry out any data processing here. For example, when a api fetch returns data, modelling the raw data into props to be passed to children
this.setState() is not allowed in this function , it is to be done in componentWillReceiveProps or componentDidUpdate
componentDidUpdate(prevProps,prevState)
Invoked right after the changes are pushed to the DOM
I have used it whenever the required data is not at the first render (waiting for api call to come through) and DOM requires to be changed based on the data received
Example, based on the age received show the user if he is eligible for application for an event
componentWillUnmount()
As the official docs mentions, any event listeners or timers used in the component to be cleaned here
In the moment of uncertainty, how would I know if the answer lies in
the lifecycle methods?
What analogy i suggest
Change is triggered in the component itself
Example, Enable editing of fields on click of an edit button
A function in the same component changes the state no involvement of lifecycle functions
Change is triggered outside of the component
Example, api call finished , need to display the received data
Lifecycle methods for the win.
Here are some more scenarios -
Does the change in state/props requires the DOM to be modified?
Example, if the current email is already present , give the input class an error class.
componentDidUpdate
Does the change in state/props requires to data to be updated?
Example, parent container which formats data received after api call and passes the formatted data to children.
componentWillUpdate
Props being passed to a child are changed , child needs to update
Example,
shouldComponentUpdate
Adding an event listener
Example, add a listener to monitor the DOM, based on window size.
componentDidMount
'componentWillMount' , to destroy the listner
Call api
'componentDidMount'
Sources -
Docs - https://facebook.github.io/react/docs/component-specs.html
this scotch.io article which cleared the lifecycle concepts
Event Listener - https://facebook.github.io/react/tips/dom-event-listeners.html
Some typical use cases for the most commonly used lifecycle methods:
componentWillMount: Invoked before initial rendering. Useful for making AJAX calls. For instance, if you need to grab the user information to populate the view, this is a good place to do it. If you do have an AJAX call, it would be good to render an indeterminate loading bar until the AJAX call finishes. I've also used componentWillMount to call setInterval and to disable Chrome's drag and drop functionality before the page renders.
componentDidMount: Invoked immediately after the component renders. Useful if you need to have access to a DOM element. For instance I've used it to disable copy and pasting into a password input field. Great for debugging if you need want to know the state of the component.
componentWillReceiveProps: Invoked when component receives new props. Useful for setting the state with the new props without re-rendering.
componentWillReceiveProps is part of Update lifce cycle methods and is called before rendering begins. The most obvious example is when new props are passed to a Component. For example, we have a Form Component and a Person Component. The Form Component has a single that allows the user to change the name by typing into the input. The input is bound to the onChange event and sets the state on the Form. The state value is then passed to the Person component as a prop.
import React from 'react';
import Person from './Person';
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = { name: '' } ;
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ name: event.currentTarget.value });
}
render() {
return (
<div>
<input type="text" onChange={ this.handleChange } />
<Person name={ this.state.name } />
</div>
);
}
}
Any time the user types into the this begins an Update for the Person component. The first method called on the Component is componentWillReceiveProps(nextProps) passing in the new prop value. This allows us to compare the incoming props against our current props and make logical decisions based on the value. We can get our current props by calling this.props and the new value is the nextProps argument passed to the method.