PureComponent
as we have learned in the previous chapter, which might be useful in some cases. The foundational logic remains though. A state change leads to a re-render of a component and all of its children, except from those cases in which the children are actually a PureComponent
or its call is surrounded by a React.memo()
call.ReactDOM.render()
to update our interface, and that components manage their own re-renders independently.this.state
. It is encapsulated to the component and neither parent or child components can access it.getDerivedStateFromProps()
.this.state
. This can either be done via the constructor of a Class component:@babel/plugin-proposal-class-properties
(pre-Babel 7: babel-plugin-transform-class-properties
):this.state
. While it is possible to mutate this.state
directly, it is actively discouraged.this.setState()
should be used to achieve this. By calling this.setState()
React knows to execute lifecycle methods (for example componentDidUpdate()
) and thus re-render the component. If we were to change state directly, for example by using this.state.counter = 1;
, nothing would happen initially as the render process would not be triggered. React would not know about its change in state.this.setState()
method might look a little complex at the start. This is also due to the fact that old state is not simply replaced by new state, triggering a re-render, but because many other things take place behind the scenes. Let's take a look at it step by step.null
if nothing should change. If you happen to have the same property name in your object as you do in state, the state value will be overwritten while all other properties will remain the same. To reset properties in state, their values need to be explicitly set to null
or undefined
. The new state that is being passed is thus never replaced but merged together with the existing state.counter
property whose initial value was 0
. Assuming that we want to change this state and add another date
property to pass the current date, we can construct the new object like so:this.state
right after calling setState()
only to realize that their state is still old.setState()
calls and does not immediately invoke them to avoid an unnecessary amount of re-renders. Sequential setState()
calls which are called right after each other are executed as a batch process. This is important to keep in mind as we cannot reliably access new state with this.state
just after a setState()
call.state
counter three times in quick succession. Intuitively, we might feel compelled to write the following code:0
, what will the new state be? What do you think? 3
? Nope. It's 1
! But why? React uses its batching mechanism to cluster these setState()
calls together in order to avoid a jarring user interface which continually updates. The above code snippet could be translated into the following code if it was written in functions.counter
property overwrites itself after each batch update but always uses this.state.counter
as its base reference for incrementing by 1. After all state calls having executed, React calls the render()
method again.this.state.counter
is 3
in this case, as we expected, because the state
parameter which we provide to the updater function is accessing the current state. While this is possible in theory, it is not exactly recommended. Values should be collected first and then batch processed in a single setState()
call. This avoids unnecessary re-renders with potentially outdated state.setState()
call. This parameter is a callback function which is called after the state has updated so that we can safely access the state we have just modified.render()
method of a parent component being part of the returned component tree. The component's lifecycle ends if it is removed from the tree of components supposed to be rendered. Additionally, there are lifecycle methods that react to updates or errors as well as being "unmounted".constructor(props)
static getDerivedStateFromProps(nextProps, prevState)
componentWillMount(nextProps, nextState)
(deprecated in React 17)render()
componentDidMount()
forceUpdate()
method could be invoked.componentWillReceiveProps(nextProps)
(deprecated in React 17)static getDerivedStateFromProps(nextProps, prevState)
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate(nextProps, nextState)
(deprecated in React 17)render()
getSnapshotBeforeUpdate(prevProps, prevState)
componentDidUpdate(prevProps, prevState, snapshot)
setTimeOut()
or setInterval()
calls which had been added during mounting of the component:componentWillUnmount()
componentDidCatch()
componentDidCatch()
are commonly called error boundaries and help to visualize an alternative to the erroneous tree of components. It could be a high-level component (with regard to its position in the component hierarchy) that displays an error page and asks the user to reload. But equally, it could also be a low level component which only renders a little error message next to a button, triggered by an erroneous action attached to the button.componentDidMount()
method. A re-render is triggered and the current time is shown again.componentDidMount()
and componentWillUnmount()
are being used in the above example. Default state is defined with the property date
and holds an instance of the date object. When the component mounts (componentDidMount()
) the setInterval()
interval is started and its id is saved within the instance property this.intervalId
. As the interval invokes the setState()
method every second, the component regularly triggers a re-render meaning the render()
method is called again and shows the current time again.setState()
method of the component. Depending on how deeply interlinked the function and the component are, React determines if function calls should be stopped or not once the component is no longer needed. In the case of the setInterval()
function, React does not, and we have to take care of stopping the component ourselves. Luckily React provides a method which enables us to do just that: componentWillUnmount()
.clearTimeOut()
is invoked and we pass the function the interval id which we previously saved in in the instance property.this.setState()
on an already removed component:ReactDOM.render()
once. The component takes care of the rest and initiates the render process once its state has updated. This is the normal procedure when developing applications with React. A single ReactDOM.render()
call is enough for the app to manage itself, allow interaction with the user and react to state changes and re-render the interface.DateTime
component is our logic component (smart component) and takes care of "getting" the time and updating it. The layout component on the other hand deals with the actual display of the date (showDate
) and the time (ShowTime
) via the props it has been passed. The layout component is implemented as a simple Function component as a Class component would have been unnecessarily complex and produced too much overhead.componentDidMount()
and componentWillMount()
. React also recognizes these if they have been implemented within a Class component.constructor(props)
constructor
of the ParentComponent
component. React processes components in the tree from "the outside to the inside". The further up the component is in the component hierarchy, the earlier it will be instantiated. Afterwards, its render()
method is called. This is necessary as React would not know otherwise which child components actually need processed and included in the component tree. React only runs the lifecycle methods for those components which have actually been included in the render()
method of their parent component.React.Component
or React.PureComponent
) via super(props)
. If omitted, this.props
in the constructor would be undefined leading to unexpected bugs and behavior.this.state = { }
) and bind the instance methods to their respective class instances with .bind()
(for example this.handleClick = this.handleClick.bind(this)
). This is necessary as instance methods would otherwise lose their context within the component as their event listeners are used inside JSX and this
would not point to the instance of the component anymore.static getDerivedStateFromProps(nextProps, prevState)
getDerivedStateFromProps()
method. As can be inferred from its name, this is a static method and as does not have access to the component instance via this
. Its primary goal is to calculate the next state of the component based on the props it has been passed and its last state. It is returned as an object. If no changes need applied to the state, null
is returned instead. The method's behavior is identical to that of this.setState()
and only updates those parts of the state which are part of the returned object. Those properties are merged along with the last state into a new state.componentWillReceiveProps
. Other than the previous method, it does not have access to the component instance. The React core team has explained that the former can lead to unexpected behavior in asynchronous rendering of components and has thus marked it "unsafe". The same applies to componentWillMount()
and componentWillUpdate()
. While the term might be associated with security breaches, it actually means something a little different. Components using this lifecycle method could lead to bugs and other side effects after React version 17.getDerivedStateFromProps()
should not introduce any side effects (for example it should not trigger any XHRequests) and only derive the new state of the component instance based on its current props. In contrast to the constructor, this method is is not only called during the mounting phase of the component but also if the component receives new props. In order for that to happen the props do not have to have changed their content.render()
render()
method which describes our user interface and the child components to render. The above example only contains one child component: the ChildComponent
.constructor()
, getDerivedStateFromProps()
and then the render()
method of our child component is called just the same as was the case in the ParentComponent
. The Child component in our example does not have any other children implying that no other elements are rendered. If it did, their lifecycle methods would also be run until React would find a component which does not return any other React components anymore. It would simply contain DOM elements like div
, p
, section
and span
etc (and of course any combination of these), null
or an array which in turn would not contain any other components.componentDidMount()
componentDidMount()
method enters the scene once such a component is reached. It is called as soon as a component and all of its children have been rendered. From now on, we can also access the DOM Node of the component if necessary or start intervals or timeouts or initiate network requests via XHR/fetch. The componentDidMount()
method is the best place for these.componentDidMount()
of the ChildComponent
is called and only then ParentComponent
's componentDidMount()
is invoked.setTimeOut()
within the ParentComponent
which modifies the state of our component every 2000 milliseconds. It demonstrates which lifecycle methods are being called during the update of a component. Any other changes on the state of the mounted component are no longer part of the mounting phase but part of the update phase. This phase is entered after the first 2000 milliseconds once the ParentComponent
modifies its own state via this.setState()
.shouldComponentUpdate(nextProps, nextState)
shouldComponentUpdate()
is called. But beware: there is a difference depending on whether the props or the state changed: if a component receives new props from the outside, getDerivedStateFromProps()
is called shortly beforehand.shouldComponentUpdate()
method enables us to inform if a costly re-render is actually necessary. The method receives the next props and the next state as a parameter and can determine, based on those, whether a re-render should take place. The method either has to return true
to trigger the re-render or false
which will prohibit the calls of componentDidUpdate()
, getSnapshotBeforeUpdate()
as well as render()
.shouldComponentUpdate()
can be helpful as to optimize the rendering performance by preventing further re-renders.false
from our ParentComponent
's shouldComponentUpdate()
method, our logging output would be much shorter. Lines 14-18 would simply be missing. The component itself would not re-render, the render()
method would not be called and the ChildComponent
would also not re-render as well as update itself.true
is returned which in turn calls the render()
method of the ParentComponent
. This triggers another re-render of the ChildComponent
which receives new props which mirror the ParentComponent
updated state. And just like that we find ourselves in the update cycle of the ChildComponent
.getDerivedStateFromProps()
derives a new state based on the new props. Afterwards shouldComponentUpdate()
is called. This is where we can check whether the component's relevant props have actually changed and if they did not, we could prohibit the re-render by returning false
from shouldComponentUpdate()
. If we did not do that, the obligatory call of the render()
method would follow. Let's look at the next lifecycle method that would occur next in our component lifecycle.getSnapshotBeforeUpdate(prevProps, prevState)
getDerivedStateFromProps()
to better deal with asynchronous rendering in React. It receives the last props and the last state and has access to the current state of the HTML DOM before React applies any modifications from the last render()
cycle.getSnapshotBeforeUpdate()
can be really useful. It can return any value or null
and its return value can be passed to componentDidUpdate()
as a third parameter.getSnapshotBeforeUpdate()
is rarely used. It might even be the least used out of all the lifecycle methods as we rarely need to access DOM elements directly. Most problems that used to be solved by manipulating the DOM API in an imperative fashion, can now be solved directly in the abstract component tree with JSX.componentDidUpdate(prevProps, prevState, snapshot)
componentDidUpdate()
forms the last method of the update cycle. It is called after getDerivedStateFromProps()
has derived the new props, shouldComponentUpdate()
has returned true
and after getSnapshotBeforeUpdate()
has created the last snapshot of the latest condition of the DOM.getSnapshotBeforeUpdate()
method, its return value will be passed as a third parameter.componentDidMount()
, componentDidUpdate()
is also resolved from the "inside to the outside". First, the componentDidMount()
methods of the child components are called, then those of the parents. componentDidUpdate()
is the perfect place to trigger side effects, for example starting XHRs if certain properties of the components have changed. This can easily be checked with a simple comparison between the current props and the last props (which we have received as a parameter) or the current state and the last state.render()
method.componentDidUpdate()
the update cycle has also come to a finish. While the mounting cycle is only ever run once, namely when the component first renders, the update cycle can be triggered an infinite number of times: as soon as the component changes its state or receives new props.componentWillUnmount()
componentWillUnmount()
is only ever run if a component is completely removed from the DOM. This has not happened in our example. A component counts as "unmounted" after it has been explicitly removed by calling ReactDOM.unmountComponentAtNode()
(this is particularly important for mount nodes) or if it is not implicitly returned from the render()
method of its parent component anymore.componentWillUnmount()
will be called but of course only if it has been manually implemented. This is true for most lifecycle methods apart from render()
. The componentWillUnmount()
lifecycle method is an essential tool to "clean up" our application. It is the place where functions can and should be called to ensure that no traces are left behind. "Traces" can refer to timeouts we are still waiting on (setTimeout
) or intervals which are still running (setInterval
) but also DOM modifications which have taken place outside of our component JSX, as well as network requests which are still ongoing (XHR/Fetch calls) or simply event listeners which were added to the DOM via the API method Element.addEventListener()
.addEventListener()
is almost not necessary anymore in React as React introduces its own event system to aid readability and consistency.