How to animate a React component on render? - reactjs

I am trying to animate a React component that contains data fetched from elsewhere. Placing it in a ReactCSSTransitionGroup worked fine. That is, until I altered the component's render() method to return false until the data has been fetched (to prevent it from being rendered without data).
Now, I guess the component is mounted immediately, at which point the animation classes are added, but only rendered afterwards. Is this thinking correct? How can I get the component to be animated when render returns the actual component?

ReactCSSTransitionGroup activates whenever components are added and deleted to its props.children. Since your component is mounted before you fetch data, nothing will happen after data is fetched (I think this is true even if the component's render() method returns false. Let me know in the comments if that is incorrect)
Here's a solution
Just don't mount the component (in the solution, it's a <div key="1"> tag) until the react class receives the data. Use component states on the parent component to keep track of the state of your asynchronous request.

The ReactCSSTransitionGroup doesn't play nicely with tables as it's default behaviour is to wrap tags with a span element. You can provide it with your own component, but I found the solution quite heavy and complex.
I have a different approach that allows a React component to animate each time its content changes. The animation is continually triggered by toggling between 2 duplicate CSS styles.

Other than the ReactCSSTransitionGroup, another way is to write your own css transitions with an 'enter' class that is added to the component in componentdidmount. Keep in mind that you should change the state in a requestAnimationFrame because otherwise your class would be added in the same event loop as it is mounted, thus wont animate. Here's an example:
https://codesandbox.io/s/zkm5015y1x
Also, more on event loop, a talk by Jake Archibald:
https://www.youtube.com/watch?v=cCOL7MC4Pl0

Related

Will componentWillMount run again if component is re-rendered because of parent component?

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.

React: Parent component re-renders all children, even those that haven't changed on state change

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

Child React Component will not Re-Render

I'm trying to create a Word Clock in React ... However, my child component does not re-render, despite me sending in a new prop. In order for the correct time to show up I need to refresh the page ...
I read about the component life cycle and I thought I was doing it right but it seems not. My guess is that when I render in parent component that this only renders it once and does not re-render even though I am constantly updating the state of this by setState() every second and updating the time ...
Here is the code: Not working demo, just the app.js file I'm using Facebook's Create-react-app
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
https://jsfiddle.net/dLtedtm0/
It would help if you could provide some documentation inside your code, to explain what the code is supposed to do.
That said, here are some tips to get you going in the right direction:
In your <Text>, you have a componentWillReceiveProps(nextProps) function, which calls highlight(). The highlight function changes state, based on props. However: at this point props still have the old values (because you are inside componentWillReceiveProps(). To solve, you could pass nextProps to highlight. However, I would advise to remove the state from the Text component entirely.
Your <Text> component has state, but it does not need it: all the different variants of the component are determined by props. So it would be much simpler to put the logic to translate props to whatever you render inside your render function. Then the only lifecycle method in your text component would be render(). State is really intended for values that the component itself will change during the lifecycle of the component (e.g. user input in a text field).
In your clock, in get getInitialState() you only return a showValue. Later in your code, you also include hours, minutes, seconds to state. Better to include all stuff in getInitialState() with some initial value, so readers of your code will know what you manage in the state of this component.
Hope this will help you in the right direction..
UPDATE: As a starting point, you can find a simplified working codepen here, with just the clock component.
It has state (current time), and simply renders the time every second. Hopefully you can start adding code from there.

When exactly is `componentDidMount` fired?

This is a recurring problem I have with React. The componentDidMount method is guaranteed to be fired when the component is rendered for the first time so it seems like a natural place to take DOM measurements like heights and offsets.
However, many times I receive wrong style readings at this point of the component's lifecycle. The component is in the DOM, when I break with the debugger, but it's not yet painted on the screen. I get this problem with elements that have width/height set to 100% mostly.
When I take measurements in componentDidUpdate - everything works fine, but this method will not fire at the initial render of the component.
So my question is - when exactly is componentDidMount fired because it's obviously not fired after all the browser paints are done.
EDIT:
This Stackoverflow issue deals with the same subject:
It also references this github conversation that explains what happens
Inside a react component tree, componentDidMount() is fired after all children components have also been mounted. This means, that any component's componentDidMount() is fired before its parent has been mounted.
So if you want to measure DOM position and sizes etc, using componentDidMount() of a child component is an unsafe place/ time to do this.
In your case: to get accurate reading from 100% width/height components, a safe place to take such measurements would be inside the componentDidMount() of the top react component.
100% is a width/height relative to the parent/ container. So measurements can only be taken after the parent has been mounted too.
As you may know, componentDidMount is triggered only once immediately after the initial rendering.
Since you are taking measurements, it would seem you would want to also trigger your measuring when componentDidUpdate in case your measurements change when your component updates.
Please note that componentDidUpdate does not occur for the initial render so you likely need both lifecycle events to trigger your measurement handling. See if this second event triggers for you and if it has different measurements.
In my opinion you should avoid using setTimeout or requestAnimationFrame when possible.
React Lifecycle Reference.
It is called only once when the component mounted. That’s the perfect time to do an asynchronous request to fetch data from an API. The fetched data would
get stored in the internal component state to display it in the render() lifecycle method.
Simple: It Run After Render Functions
If you want to respond to something being mounted in the DOM, the most reliable way to do that is with a ref callback. For example:
render() {
return (
<div
ref={(el) => {
if (el) {
// el is the <div> in the DOM. Do your calculations here!
}
}}
></div>
);
}
you can try delaying logic in componentDidMount() with requestAnimationFrame(). the logic should occur after the next paint.
however, we'd need to know more about your code to see why the nodes haven't been painted. i've never hit that problem. componentDidMount() fires right after the dom nodes are added to the page, but not necessarily after they have been painted.

Call custom method on component when props update

I have a YouTubeiFrame react component that renders an empty div and then drops the YouTube iframe player in to that div using componentDidMount. All works fine. I have a cueVideo method defined on the component which uses the players
API to cue new videos:
cueVideo(videoId) {
this.player.cueVideoById(videoId)
}
In different places in my app you can click a button or some another action to indicate that you want to play a new video. When you perform this action the 'currentVideo' state is updated via redux and as a result the YouTubeiFrame component receives the new video id as an updated prop.
My question is how to call the cueVideo method above in reaction to the updated prop. I've considered using shouldComponentUpdate to compare this.props.currentVideo with the next props
but concious this method is really supposed to return true/false so the render function can be called. All I want to do is call cueVideo when the currentVideo prop changes - I don't really need to re-render.
What's the best way to do this?
All I want to do is call cueVideo when the currentVideo prop changes - I don't really need to re-render.
When new props are received, React will automatically call render() to update your component. Once all the updates have been flushed to the DOM, React will also call componentDidUpdate() on your component to give you an opportunity to respond to the changes. As the documentation says:
Use this as an opportunity to operate on the DOM when the component has been updated.
I would suggest this is the perfect place to call your code as needed.

Resources