I'm using React.js, and as you know, componentWillMount() is going to be deprecated.
I wanna replace my componentWillMounts.
I'm going to move its logics into constructor. Is there any difference between executing some logic in componentWillMount and in constructor?
For example,
before
class Hello extends React.Component {
componentWillMount() {
doSomething();
}
render() {
return <div>{this.state.name} </div>
}
}
after
class Hello extends React.Component {
constructor(props) {
super(props);
doSomething();
}
render() {
return <div>{this.state.name} </div>
}
}
Also, when doSomething is setState, is there any difference setting state in constructor and in public prop?
in constructor
constructor(props) {
super(props);
this.state = { foo: 1 };
}
in public prop
state = { foo: 1 };
constructor is not the right place for performing some actions. Because it will hold other operations until it's finished.
componentDidMount is the right choice because it's an asynchronous function so that actions are run in the background and there'll be no hamper in the UI rendering.
Here's a list that you can choose when to use between constructor and componentDidMount:
constructor
Do:
initialize state
bind event handlers
If you don't initialize a state and you don't bind methods, you don't need to implement the constructor.
Don't:
Avoid introducing any side-effects or subscriptions. Do not set state by using setState() in the constructor.
componentDidMount
Do:
initialization that requires DOM nodes
load data from a remote endpoint (where to instantiate the network request)
set up any subscriptions (don’t forget to unsubscribe in componentWillUnmount())
You may also be interested to read the comment from the creator of react, Dan Abramov:
I won't like to wait for the component to be mounted to dispatch an ajax call to fulfill the component data dependencies. I would like to do it as soon as possible, like in the constructor, not even in componentWillMount.
If it's an async request, it won't be fulfilled by the time the component mounts anyway, regardless of where you fire it. This is because JS is single threaded, and the network request can't "come back" and be handled while we are still rendering. So the difference between firing it earlier and later is often negligible.
You're right that it matters in some rare cases though and for those cases it might make sense to break the recommendation. But you should be extra cautious as state can update before mounting, and if your data depends on state, you might have to refetch in that case. In other words: when in doubt, do it in componentDidMount.
The specific recommendation to avoid side effects in the constructor and Will* lifecycles is related to the changes we are making to allow rendering to be asynchronous and interruptible (in part to support use cases like this better). We are still figuring out the exact semantics of how it should work, so at the moment our recommendations are more conservative. As we use async rendering more in production we will provide a more specific guidance as to where to fire the requests without sacrificing either efficiency or correctness. But for now providing a clear migration path to async rendering (and thus being more conservative in our recommendations) is more important.
For further interest, you may also visit this post.
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
So If you want to perform some action just once, on before mount of component, then getDerivedStateFromProps is not appropriate option.
Use componentDidMount method.
Have more details in https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
Use getDerivedStateFromProps() it's next method called after constructor.
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
setState in a constructor will perform unnecessary computation, you can either use the state property as you suggest or this.state = { ... } in your constructor if you need to perform more computation or access props.
The React documentation recommends you use constructor (or a class property) over componentWillMount for initialising state. For side-effects (e.g. http request) that could update state you should consider componentDidMount or componentDidUpdate. With asynchronous state updates, you should always make sure that your component handles the state without that data.
Related
I'm trying to find a way to measure the time certain components take to mount, taking into consideration than the library for measuring is asynchronous (firebase-performance) to be specific.
I need to find out how long a page consisting of multiple asynchronously mounted components takes to render. I am not sure, how I should go about tracking the time between the time component is invoked (function) and the time it takes to mount (render).
I'm slightly confused by the life-cycle methods, as componentWillMount is considered deprecated. Basically, my understanding would be, that in order to track the time component takes to render would be the following.
class Example extends Component<Props> {
async componentDidMount() {
// let's just say this takes 3 seconds as an external API call
await exampleCall()
}
render() {
return <h1>Test</h1>
}
}
And in order to measure the performance of this component in a easily reproducable way, I wanted to construct a wrapper for the component
import perf from '#react-native-firebase/perf'
const withPerformance = (PassedComponent) => {
class Tracker extends Component<Props> {
constructor(props) {
super(props)
console.log('constructor fired')
this.trace = null
}
async componentWillMount() {
const trace = await perf.stratTrace('perf_test')
console.log('Started perf tracking')
this.trace = trace
}
async componentDidMount() {
if (this.trace) {
console.log('Stopped tracking perf')
await this.trace.stop()
} else {
console.log('Mounted, but tracking did not instantiate')
}
}
render() {
return <PassedComponent {...this.props} />
}
}
return Tracker
}
So in order for my perf tracking needs, I could just wrap my existing components with the following wrapper
// last line of Example.js:
export default withPerformance(Example)
This however results, when opening the remote debugger in my simulator with very confusingly:
constructor fired [constructor]
Mounted, but tracking did not instantiate [componentDidMount]
Started perf tracking [componentWillMount]
And my firebase console is empty with no custom traces.
This is confusing and wrong for multiple reasons, but the worst culprit is obviously the breaking of the flow of the lifecycle methods, which should be as follows:
But in my case, it seems to be constructor -> CDM -> CWM
This seems to seemingly break the component lifecycle as componentWillMount somehow fires after componentDidMount (albeit this might be due to the asynchronous nature), which is not ideal to begin with.
componentWillMount is a deprecated lifecycle method to begin with, but there seems to be no replacement for it introduced where I could begin any asynchronous subscriptions before the actual DOM nodes are loaded.
I am forced to use asynchronity in the lifecycle methods, which is not ideal to begin with.
As of note, I am aware of the Profiler introduced in react, but I need this to be able to work in production as well, which right now is not recommended with the Profiler.
Any idea how I should go about doing this?
I did experiment with manual tracking with performance.now() which does work, but it's a client requirement (not in an architectural, but actual client sense) does want the integration to work with the firebase performance tracking.
I created a a box similar to twitter using react. I was looking at the react documentation found several component life cycles but not sure which one I should use to improve my code performance: componentDidMount or componentWillMount?
When I type something in my text box I see an update in the console printing the text box value. Can anyone help me understand which method to use and when in this case?
https://jsfiddle.net/c9zv7yf5/2/
class TwitterBox extends React.Component {
constructor(props) {
super(props);
this.state = { enteredTextBoxvalue : '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({enteredTextBoxvalue: event.target.value});
if((event.target.value).length > 3) {
this.setState({className : 'wholeContainer'});
//console.log("long characters");
}
}
render() {
return (<div>Hello {this.props.name}
<textarea className={this.state.className}
value={this.state.enteredTextBoxvalue}
onChange = {this.handleChange}>
there should be only 140 characters
</textarea>
</div>);
}
}
ReactDOM.render(
<TwitterBox name="World" />,
document.getElementById('container')
);
componentWillMount is called right before the component gets rendered.
componentDidMount is called right after the component gets rendered.
If you need to prepare data you use componentWillMount.
componentDidMount is popularly used among sending api calls or grabbing data just right after the component renders and is highly recommended to use that.
componentWillMount:
This function is called right before the component’s first render, so at first glance it appears to be a perfect place to put data fetching logic
componentDidMount:
Using componentDidMount makes it clear that data won’t be loaded until after the initial render. This reminds you to set up initial state properly, so you don’t end up with undefined state that causes errors.
As part of your question is about performance you could consider also having a look at shouldComponentUpdate to avoid reconciliation.
componentWillMount is invoked immediately before mounting occurs. It is called before render().
componentDidMount is invoked immediately after a component is mounted.
componentWillMount
component is about to render, plays the same role as constructor
there is no component in DOM yet you cannot do anything involving DOM
manipulation
calling setState() synchronously will not trigger
re-render as the component is not rendered yet
I would not recommend calling async /api requests here (technically there is no guaranty they will finish before component will be mounted, in this case the your component will not be re-rendered to apply those data)
componentDidMount
component has been rendered, it already seats in the DOM
you can perform manipulations involving DOM elements here (e.g. initialize third-party plugin)
call async /api requests, etc.
I'm working on react for last 1 year. The convention which we follow is make an API call in componentDidMount, fetch the data and setState after the data has come. This will ensure that the component has mounted and setting state will cause a re-render the component but I want to know why we can't setState in componentWillMount or constructor
The official documentation says that :
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method will
not trigger a re-rendering. Avoid introducing any side-effects or
subscriptions in this method.
it says setting state in this method will not trigger a re-rendering, which I don't want while making an API call. If I'm able to get the data and able to set in the state (assuming API calls are really fast) in componentWillMount or in constructor and data is present in the first render, why would I want a re-render at all?
and if the API call is slow, then setState will be async and componentWillMount has already returned then I'll be able to setState and a re-render should occur.
As a whole, I'm pretty much confused why we shouldn't make API calls in constructor or componentWillMount. Can somebody really help me understand how react works in such case?
1. componentWillMount and re-rendering
Compare this two componentWillMount methods.
One causes additional re-render, one does not
componentWillMount () {
// This will not cause additional re-render
this.setState({ name: 'Andrej '});
}
componentWillMount () {
fetch('http://whatever/profile').then(() => {
// This in the other hand will cause additional rerender,
// since fetch is async and state is set after request completes.
this.setState({ name: 'Andrej '});
})
}
.
.
.
2. Where to invoke API calls?
componentWillMount () {
// Is triggered on server and on client as well.
// Server won't wait for completion though, nor will be able to trigger re-render
// for client.
fetch('...')
}
componentDidMount () {
// Is triggered on client, but never on server.
// This is a good place to invoke API calls.
fetch('...')
}
If you are rendering on server and your component does need data for rendering, you should fetch (and wait for completion) outside of component and pass data thru props and render component to string afterwards.
ComponentWillMount
Now that the props and state are set, we finally enter the realm of Life Cycle methods
That means React expects state to be available as render function will be called next and code can break if any mentioned state variable is missing which may occur in case of ajax.
Constructor
This is the place where you define.
So Calling an ajax will not update the values of any state as ajax is async and constructor will not wait for response. Ideally, you should use constructor to set default/initial values.
Ideally these functions should be pure function, only depending on parameters. Bringing ajax brings side effect to function.
Yes, functions depend on state and using this.setState can bring you such issues (You have set value in state but value is missing in state in next called function).
This makes code fragile. If your API is really fast, you can pass this value as an argument and in your component, check if this arg is available. If yes, initialise you state with it. If not, set it to default. Also, in success function of ajax, you can check for ref of this component. If it exist, component is rendered and you can call its state using setState or any setter(preferred) function.
Also remember, when you say API calls are really fast, your server and processing may be at optimum speed, but you can never be sure with network.
If you need just data only at first run and if you are ok with that. You can setState synchronously via calling a callback.
for eg:
componentWillMount(){
this.setState({
sessionId: sessionId,
}, () => {
if (this.state.hasMoreItems = true) {
this.loadItems() // do what you like here synchronously
}
});
}
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.
I know that an error is thrown when setting state for a component not yet mounted. Which explains the error I get from using setState function as oppose to explicitly and directly setting the state.
import React, {Component} from 'react';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {term: ''}; // -> seems to be the agreed means to set initial state
// this.setState({term: ''}); // -> generates an error
}
render() {
return (
<div>
<input onChange={event => this.setState({term: event.target.value})}/>
Value of the input: {this.state.term}
</div>
);
}
}
The error I get when I uncomment the second line this.setState({term: ''}) is:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the component.
I know how to prevent the error, simply by setting state explicitly without telling React anything about it and I have already seen the github issue talking about the bug: Github Issue #3878 What I want to know though is why cant React work it out? if one calls setState from a constructor it knows this is the first time its being used? I am probably simplifying it way too much, but if anyone has a nice technical answer as the reason why not?
React classes have always initialized with a property called state set to a value of null as seen in the source code. As you know, React provides a setState method for manipulating this property. According to the docs:
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().
In short, setState() is an asynchronous, multi-step, unpredictable operation that will cause component rerenders. To call such a function would require an object to already be fully initialized and thus couldn't happen while the class is still mounting. It would already try to perform lifecycle operations on the class before it was fully initialized.
Of course, that leaves an issue if you want a component to start out with a state that is not null and yet don't want to immediately cause multiple renders and operations to take place. That is why React provides a way to initialize a component state without relying on setState. In ES5, this was setting the initial state inside of a property called getInitialState. However, ES6 introduced a native syntax for setting properties when a class is initialized with a special constructor method (so React no longer had need of its own custom version). That is why, if you want to initialize a React component with a state when it is mounting, you must declare it as this.state = {} and not use setState().