When you use setState(), then apart from assigning to the object state React also re-renders the component and all its children. You would get error like this: Can only update a mounted or mounting component. So we need to use this.state to initialize variables inside constructor.
React Interview Questions
Yes, it is safe to use
setState()insidecomponentWillMount()method. But at the same it is recommended to avoid async initialization incomponentWillMount()lifecycle method.componentWillMount()is invoked immediately before mounting occurs. It is called beforerender(), therefore setting state in this method will not trigger a re-render. Avoid introducing any side-effects or subscriptions in this method. We need to make sure async calls for component initialization happened incomponentDidMount()instead ofcomponentWillMount().componentDidMount() { axios.get(`api/todos`) .then((result) => { this.setState({ messages: [...result.data] }) }) }If the props on the component are changed without the component being refreshed, the new prop value will never be displayed because the constructor function will never update the current state of the component. The initialization of state from props only runs when the component is first created.
The below component won't display the updated input value:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { records: [], inputValue: this.props.inputValue, }; } render() { return <div>{this.state.inputValue}</div>; } }Using props inside render method will update the value:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { record: [], }; } render() { return <div>{this.props.inputValue}</div>; } }Question 349
How you use decorators in React?
You can decorate your class components, which is the same as passing the component into a function. Decorators are flexible and readable way of modifying component functionality.
@setTitle("Profile") class Profile extends React.Component { //.... } /* title is a string that will be set as a document title WrappedComponent is what our decorator will receive when put directly above a component class as seen in the example above */ const setTitle = (title) => (WrappedComponent) => { return class extends React.Component { componentDidMount() { document.title = title; } render() { return <WrappedComponent {...this.props} />; } }; };Note: Decorators are a feature that didn't make it into ES7, but are currently a stage 2 proposal.
Question 350
What is CRA and its benefits?
The
create-react-appCLI tool allows you to quickly create & run React applications with no configuration step.Let's create Todo App using CRA:
# Installation $ npm install -g create-react-app # Create new project $ create-react-app todo-app $ cd todo-app # Build, test and run $ npm run build $ npm run test $ npm startIt includes everything we need to build a React app:
- React, JSX, ES6, and Flow syntax support.
- Language extras beyond ES6 like the object spread operator.
- Autoprefixed CSS, so you don’t need -webkit- or other prefixes.
- A fast interactive unit test runner with built-in support for coverage reporting.
- A live development server that warns about common mistakes.
- A build script to bundle JS, CSS, and images for production, with hashes and sourcemaps.
The lifecycle methods are called in the following order when an instance of a component is being created and inserted into the DOM.
constructor()static getDerivedStateFromProps()render()componentDidMount()
The following lifecycle methods going to be unsafe coding practices and will be more problematic with async rendering.
componentWillMount()componentWillReceiveProps()componentWillUpdate()
Starting with React v16.3 these methods are aliased with
UNSAFE_prefix, and the unprefixed version will be removed in React v17.The new static
getDerivedStateFromProps()lifecycle method is invoked after a component is instantiated as well as before it is re-rendered. It can return an object to update state, ornullto indicate that the new props do not require any state updates.class MyComponent extends React.Component { static getDerivedStateFromProps(props, state) { // ... } }This lifecycle method along with
componentDidUpdate()covers all the use cases ofcomponentWillReceiveProps().The new
getSnapshotBeforeUpdate()lifecycle method is called right before DOM updates. The return value from this method will be passed as the third parameter tocomponentDidUpdate().class MyComponent extends React.Component { getSnapshotBeforeUpdate(prevProps, prevState) { // ... } }This lifecycle method along with
componentDidUpdate()covers all the use cases ofcomponentWillUpdate().It is recommended to name the component by reference instead of using
displayName.Using
displayNamefor naming component:export default React.createClass({ displayName: "TodoApp", // ... });The recommended approach:
export default class TodoApp extends React.Component { // ... }also
const TodoApp = () => { //... }; export default TodoApp;Recommended ordering of methods from mounting to render stage:
staticmethodsconstructor()getChildContext()componentWillMount()componentDidMount()componentWillReceiveProps()shouldComponentUpdate()componentWillUpdate()componentDidUpdate()componentWillUnmount()- click handlers or event handlers like
onClickSubmit()oronChangeDescription() - getter methods for render like
getSelectReason()orgetFooterContent() - optional render methods like
renderNavigation()orrenderProfilePicture() render()
The reason behind for this is that
setState()is an asynchronous operation. React batches state changes for performance reasons, so the state may not change immediately aftersetState()is called. That means you should not rely on the current state when callingsetState()since you can't be sure what that state will be. The solution is to pass a function tosetState(), with the previous state as an argument. By doing this you can avoid issues with the user getting the old state value on access due to the asynchronous nature ofsetState().Let's say the initial count value is zero. After three consecutive increment operations, the value is going to be incremented only by one.
// assuming this.state.count === 0 this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); // this.state.count === 1, not 3If we pass a function to
setState(), the count gets incremented correctly.this.setState((prevState, props) => ({ count: prevState.count + props.increment, })); // this.state.count === 3 as expected(OR)
React may batch multiple
setState()calls into a single update for performance. Becausethis.propsandthis.statemay be updated asynchronously, you should not rely on their values for calculating the next state.This counter example will fail to update as expected:
// Wrong this.setState({ counter: this.state.counter + this.props.increment, });The preferred approach is to call
setState()with function rather than object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument.// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment, }));The primary use case for
isMounted()is to avoid callingsetState()after a component has been unmounted, because it will emit a warning.if (this.isMounted()) { this.setState({...}) }Checking
isMounted()before callingsetState()does eliminate the warning, but it also defeats the purpose of the warning. UsingisMounted()is a code smell because the only reason you would check is because you think you might be holding a reference after the component has unmounted.An optimal solution would be to find places where
setState()might be called after a component has unmounted, and fix them. Such situations most commonly occur due to callbacks, when a component is waiting for some data and gets unmounted before the data arrives. Ideally, any callbacks should be canceled incomponentWillUnmount(), prior to unmounting.You should initialize state in the constructor when using ES6 classes, and
getInitialState()method when usingReact.createClass().Using ES6 classes:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { /* initial state */ }; } }Using
React.createClass():const MyComponent = React.createClass({ getInitialState() { return { /* initial state */ }; }, });Note:
React.createClass()is deprecated and removed in React v16. Use plain JavaScript classes instead.