I have a ScrollToTopOnMount as implemented here with
componentDidMount(prevProps) {
window.scrollTo(0, 0)
}
Which works great, except when I combine this with SSR and hydrating, my page snaps to the top when the hydration happens.
Other answers such as this one gave me hope that there may be some way to discriminate between whether a component is legitimately mounting, or just being hydrated. However, this does not seem to be the case as of react-dom 16.1.1, where both the server and the client appear to call componentWillMount.
Is there a way to have an lifecycle method determine whether it's being mounted where there is no DOM or hydrating an existing node?
ReactDOM.hydrate has a callback when it completes. One solution would be to set and un-set a global var window.hydrating = true and differentiate based on that when the component mounts.
Related
I am currently developing a react redux based web application which displays large amount of data on the UI. When the data size increases, the frame per second decreases. Also, certain forms displaying components take longer and appear to be sluggish.
If someone could guide me on correct rendering method or some coding standards needed to be followed for such applications, it will be a great help.
-Thanks
I am currently checking whether my application uses react lifecycle components (explicitly by any other developer). I am also suspecting the way in which components are rendered.
Hello and welcome to StackOverflow!
Your question is very generic, so it's hard to pinpoint exactly how to resolve it.
I guess the first thing I'd do is take a look in chrome's performance tab in the developers tools. You can use it to profile you application and see what functions take the longest.
You can find helpful information here and here.
This will give you a good starting point.
As far as profiling a React application, you can take a look at React's Dev Tool profiler, more info can be found here.
You might also want to make sure to avoid the deprecated lifecycle functions, as they are known to cause performance issues. Those are:
componentWillMount
componentWillRecieveProps
componentWillUpdate
And make sure you perform all HTTP requests after components mount.
If everything fails, you should look into memoization techniques. Memoizing is basically saving the result of a function call in memory, so the next time your function is called with the same arguments, you don't recalculate the output. For that you can use React's builtin memo feature to memoize complete components, and a selector (like reselect) to memoize redux computations.
Please Check Prevent Unnecessary Rendering
All children in component re-renders when its props or state gets updated. This is the default behavior, and, since React updates only the relevant DOM nodes, this process is usually fast. However, there are certain cases where the component’s output stays the same regardless of whether the props have changed.
To alter the default implementation, you can use a lifecycle hook called shouldComponentUpdate(). This lifecycle hook doesn’t get invoked during the initial render, but only on subsequent re-renders. shouldComponentUpdate() is triggered when the props have changed and it returns true by default.
shouldComponentUpdate(nextProps, nextState) {
return true;
}
If you’re sure that your component doesn’t need to re-render regardless of whether the props have updated or not, you can return false to skip the rendering process.
class ListItem extends Component {
shouldComponentUpdate(nextProps, nextState) {
return false
}
render() {
// Let the new props flow, I am not rendering again.
}
}
Alternatively, if you need to update only when certain props are updated, you can do something like this:
class ListItem extends Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.isFavourite != this.props.isFavourite;
}
...
}
For example here, we’re checking whether the current value of isFavourite has changed before each render (nextProps holds the new value of the prop) and if yes, the expression returns true. The component gets re-rendered. The changes in any other props won’t trigger a render() since we’re not comparing them in shouldComponentUpdate().
Attention Replacing ‘componentWillReceiveProps’ with ‘getDerivedStateFromProps’
With the release of React 16.3, some new lifecycle methods have been
introduced, and release of React 17 will deprecate some lifecycle
method.
You can find helpful information here
Will componentWillMount run again if component is re-rendered because of parent component?
No, componentWillMount is called only once.
Also, componentDidMount is called only once.
componentDidUpdate is called on every re-render.
To correctly understand about the react lifecycle methods you can go through this link.
https://engineering.musefind.com/react-lifecycle-methods-how-and-when-to-use-them-2111a1b692b1
The short answer is NO
It's called once right before your component is about to be rendered to the DOM.
The long answer is:
componentWillMount
Your component is going to appear on the screen very shortly. That chunky render function, with all its beautifully off-putting JSX, is about to be called.
Most Common Use Case: App configuration in your root component.
Can call setState: Yes, but don't. Use the default state instead.
componentDidMount
Here is where you load in your data. ComponentDidMount is also where you can do all the fun things you couldn’t do when there was no component to play with. Basically, here you want to do all the setup you couldn’t do without a DOM, and start getting all the data you need. Most Common Use Case: Starting AJAX calls to load in data for your component.
componentWillReceiveProps
Perhaps some data that was loaded in by a parent component’s componentDidMount finally arrived and is being passed down. Before our component does anything with the new props, componentWillReceiveProps is called, with the next props as the argument.
shouldComponentUpdate
shouldComponentUpdate should always return a boolean — an answer to the question, “should I re-render?” Yes, little component, you should. The default is that it always returns true. It's an awesome place to improve performance.
componentWillUpdate
Most Common Use Case: Used instead of componentWillReceiveProps on a component that also has shouldComponentUpdate (but no access to previous props). It’s basically the same as componentWillReceiveProps, except you are not allowed to call this.setState.
componentDidUpdate
Here we can do the same stuff we did in componentDidMount — reset our masonry layout, redraw our canvas, etc. Basically, we use it when it's all said and done, but we don’t want to waste time to redraw the canvas every time it updates. Most Common Use Case: Updating the DOM in response to prop or state changes.
componentWillUnmount
Here you can cancel any outgoing network requests, or remove all event listeners associated with the component. Basically, clean up anything to do that solely involves the component in question — when it’s gone, it should be completely gone.
When rehydrating, React will complain when the initially rendered markup doesn't match the server markup exactly. It is however not entirely uncommon for certain components to not yield identical results clientside and serverside. Most trivially, consider a component that displays the current time:
function Now() {
return <span>{ new Date().toString() }</span>;
}
Obviously such a component would show a different value every time it's rendered, and as such React will always warn about incorrect checksums.
How can I tell React that it's okay if the client renders something differently from the server, for specific components?
To answer my own question, since React v16 the documentation says this about it:
If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.
I haven't been able to find a clear answer to this, hope this isn't repetitive.
I am using React + Redux for a simple chat app. The app is comprised of an InputBar, MessageList, and Container component. The Container (as you might imagine) wraps the other two components and is connected to the store. The state of my messages, as well as current message (the message the user is currently typing) is held in the Redux store. Simplified structure:
class ContainerComponent extends Component {
...
render() {
return (
<div id="message-container">
<MessageList
messages={this.props.messages}
/>
<InputBar
currentMessage={this.props.currentMessage}
updateMessage={this.props.updateMessage}
onSubmit={this.props.addMessage}
/>
</div>
);
}
}
The issue I'm having occurs when updating the current message. Updating the current message triggers an action that updates the store, which updates the props passing through container and back to the InputBar component.
This works, however a side effect is that my MessageList component is getting re-rendered every time this happens. MessageList does not receive the current message and doesn't have any reason to update. This is a big issue because once the MessageList becomes big, the app becomes noticeably slower every time current message updates.
I've tried setting and updating the current message state directly within the InputBar component (so completely ignoring the Redux architecture) and that "fixes" the problem, however I would like to stick with Redux design pattern if possible.
My questions are:
If a parent component is updated, does React always update all the direct children within that component?
What is the right approach here?
If a parent component is updated, does React always update all the direct children within that component?
No. React will only re-render a component if shouldComponentUpdate() returns true. By default, that method always returns true to avoid any subtle bugs for newcomers (and as William B pointed out, the DOM won't actually update unless something changed, lowering the impact).
To prevent your sub-component from re-rendering unnecessarily, you need to implement the shouldComponentUpdate method in such a way that it only returns true when the data has actually changed. If this.props.messages is always the same array, it could be as simple as this:
shouldComponentUpdate(nextProps) {
return (this.props.messages !== nextProps.messages);
}
You may also want to do some sort of deep comparison or comparison of the message IDs or something, it depends on your requirements.
EDIT: After a few years many people are using functional components. If that's the case for you then you'll want to check out React.memo. By default functional components will re-render every time just like the default behavior of class components. To modify that behavior you can use React.memo() and optionally provide an areEqual() function.
If a parent component is updated, does React always update all the direct children within that component?
-> Yes , by default if parent changes all its direct children are re-rendered but that re-render doesn't necessarily changes the actual DOM , thats how React works , only visible changes are updated to real DOM.
What is the right approach here?
-> To prevent even re-rendering of virtual DOM so to boost your performance further you can follow any of the following techniques:
Apply ShouldComponentUpdate Lifecycle method - This is applied only if your child component is class based , you need to check the current props value with the prev props value ,and if they are true simply return false.
Use Pure Component -> This is just a shorter version to above method , again works with class based components
Use React memo -> this is the best way to prevent Rerendering even if you have functional components ,you simply need to wrap your components export with React.memo like : export default React.memo(MessageList)
Hope that helps!
If parent component props have changed it will re-render all of its children which are made using React.Component statement.
Try making your <MessageList> component a React.PureComponent to evade this.
According to React docs: In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component. check this link for more info
Hope this helps anyone who is looking for the right way to fix this.
If you're using map to render child components and using a unique key on them (something like uuid()), maybe switch back to using the i from the map as key. It might solve the re-rendering issue.
Not sure about this approach, but sometimes it fixes the issue
A story:
I have a server rendering but some part of my app cannot be done there, since they use document(react-art library for drawing on a canvas).
I cannot render everything in the same from, because react will say that the code received from a server and and client one are not the same.
So my solution is to render some part of my app on the server, then render this part on a client and, in the next frame, render everything that is impossible to render on a client.
So I was thinking about using setState in componentDidMount and trigger DOM update, so it can contain rendered client part which cannot be rendered on a server, but eslint says it is not good to set state in componentDidMount. Logically I cannot say why it is bad in this case. Generally it is not good because it triggers DOM update, but in my case, this is what I actually need.
What would you suggested in this case?
Edit 1:
Fixed typo, I mean componentDidMount not componentDidUpdate.
Edit 2:
Here is the same issue, but they use changing state in componentDidMount as a workaround.
I've just coded a similar scenario and happily used componentDidMount disabling ESLint's warning. IMHO it is perfectly valid ONLY in universal rendering scenarios.
Why: Using setState in discouraged in componentDidMount because you'll trigger an additional, unnecessary render.
However in a universal scenario where you want to differ server and client behaviour, you'll want that additional render and in order to prevent 'could not reuse markup' errors, you'll want it just after react reconciles with the server rendered DOM. In brief: You'll render twice in any case.
I don't however recommend coding this behaviour in a leaf / dumb component because doing so would require the supposedly dumb component to have some knowledge of it's environment (client / server) and present a design problem. Creating a component that would set the props of the dumb component would be the obvious solution.
Try setting state in componentWillMount or componentWillUpdate.
Be careful with componentWillMount, it can also be called server side.
What i understand from your question is that after your component is mounted, data on server and client changes and you want to keep component in sync with changing data.
I recommend you have a look at the flux or redux architectures of react. For example flux is implemented in a way that when anything in component changes it triggers action and listens to the store. And when anything in store is changed then component will re render itself.
Please see this link About ReactJS - Component Life Cycle.
you should use from componentWillMount cycle. componentWillMount is executed before rendering, on both server and client side.
I want to know how to trigger components update after my app is mounted.
In React, the conventional way to "push" state onto a component is using props, not state. If your application needs to acquire information client-side and adjust component state accordingly, you should implement componentWillReceiveProps.
If you feel that the state rendered on the server and in-browser are of the same kind, and don't want the hassle of tracking which properties belong this.state v. this.props, I suggest something like the following:
componentWillReceiveProps(nextProps) {
this.setState( Object.assign({}, this.state, nextProps) );
}
This will merge the passed properties into the component state, so that they can be accessed using this.state.
Prevent usage of setState in componentDidMount.
Updating the state after a component mount will trigger a second render() call and can lead to property/layout thrashing.
The following patterns are considered warnings:
class MyComponent extends React.Component {
componentDidMount(){
this.setState({
data: data
});
}
}
The following patterns are not considered warnings:
class MyComponent extends React.Component {
constructor(){
this.setState({
data: data
});
}
componentDidMount(){
...
}
}