preface
When we used React to mount and unload a component, did we find that the mount and unload process was completed in a flash? It was very difficult to add the entry and exit animations. The react-transition-group helps us solve this problem by telling it when to mount a component, when to uninstall a component, and how long it takes to mount and uninstall the component. Then it automatically manages the stages of mounting and unmounting and exposes each stage to us. So we can do different operations at different stages to achieve the animation effect.
An overview of
React-transition-group provides the following four components:
- Transition
- CSSTransition
- SwitchTransition
- TransitionGroup
Next, a simple version of react-transition-group is implemented from two perspectives of the use and principle analysis of the four components, to help us better understand the working principle of react-transition-group.
Implement a react-transition-group from zero to one
Next, we will implement a simple version of react-transition-group with the source code
Implement a Transition manually
1.1 Look at an example
import { Transition } from 'react-transition-group';
function Demo() {
const [inProp, setInProp] = useState(false); // Controls the mount and unmount of subcomponents
return (
<div>
<Transition in={inProp} timeout={2000}>/* *Transition is a function that takes state (representing different stages of mount and unmount) **/ {state => (<h1> {state} </h1>
)}
</Transition>
<button onClick={()= >setInProp(! inProp)}> Click to Enter</button>
</div>
);
}
Copy the code
Note: when we click the button for the first time (inProp value changed from false to true), we will see the change of character in the screen: entering — when we click the button for the second time (inProp value changed from true to false) entered—exiting—exited
1.2 Core Principles:
The core logic of Transition is to expose the IN and timeout interfaces to us. We control the display of components by controlling the value of IN. During the display and disappearance of components, Transition automatically helps us manage the Transition state according to the time interval passed in. It provides the following five transition states:
- entering
- entered
- exiting
- exited
- Unmounted (This state is not passed to children because the child component content has been unmounted)
Since the children received by the Transition component can be a function, the resulting states — namely, entering, entering, exited, mentioned above — are passed to the children function as parameters when the state is changed, In this way, different styles can be rendered in children according to different states to achieve entry and exit animation.
1.3 Manually Implementing a Simple Transition
- The PropType that defines this component is as follows:
export interface TransitionPropTypes {
/** * the default value is false */
in? : boolean/** * a subcomponent that is a function or ReactNode that, if it is a function, accepts parameters other than just described in terms of entering, exiting, entering, exited states */children? : React.ReactNode | ((status: string) = > React.ReactNode)
/** * animation execution time */
timeout: number
/** * is called */ when the approach animation begins executiononEnter? :(node: Element, isAppearing: boolean) = > void
/** * call */ during the approach animation executiononEntering? :(node: Element, isAppearing: boolean) = > void
/** * call */ after the entry animation executesonEntered? :(node: Element, isAppearing: boolean) = > void
/** ** is called when the exit animation starts executingonExit? :(node: Element) = > void
/** ** is called when the exit animation is executingonExiting? :(node: Element) = > void
/** * call */ after the exit animation is executedonExited? :(node: Element) = > void
}
Copy the code
- Implement the component
// Store the constants used
export const UNMOUNTED = 'unmounted'
export const EXITED = 'exited'
export const ENTERING = 'entering'
export const ENTERED = 'entered'
export const EXITING = 'exiting'
Copy the code
export default class Transition extends Component<
TransitionPropTypes.{ status: string }
> {
static contextType = TransitionGroupContext
private nextCallback
constructor(props, context) {
super(props)
const { in: _in } = props
this.state = {
status: _in ? ENTERED : EXITED, // Used to store transition states, the initial state is EXITED}}componentDidMount() {
// This controls whether the Transition child executes the approach animation when it is first mounted
if (this.context) {
const { status } = this.context
if (status === ENTERING) {
this.updateStatus(true, status)
}
}
}
componentDidUpdate(prevProps) {
let nextStatus = null
// execute when the props value changes
if(prevProps ! = =this.props) {
const { status } = this.state
const { in: _in } = this.props
// Perform the approach animation when it becomes true
if (_in) {
/ / if the current status of EXITIING | | EXITED
if(status ! == ENTERING && status ! == ENTERED) { nextStatus = ENTERING } }else { // Perform an exit animation when it becomes false
if (status === ENTERING || status === ENTERED) {
nextStatus = EXITING
}
}
}
// Update the status
this.updateStatus(false, nextStatus)
}
// Use closure features to control callbakc can be cancelled at any time
setNextCallBack(callback) {
let active = true
this.nextCallback = (event) = > {
if (active) {
active = false
this.nextCallback = null
callback(event)
}
}
this.nextCallback.cancel = () = > {
active = false
}
return this.nextCallback
}
// To ensure that the setState asynchronous callback can be cancelled
safeSetState(state, callback) {
callback = this.setNextCallBack(callback)
this.setState(state, callback)
}
// Execute callback after the specified timeout interval
onTransitionEnd(timeout, callback) {
if(timeout ! = =null) {
callback = this.setNextCallBack(callback)
setTimeout(callback, timeout)
}
}
// Perform entry related operations
performEnter(mounting) {
// console.log(' Perform entry animation ')
const { onEnter, onEntering, onEntered, timeout } = this.props
// eslint-disable-next-line react/no-find-dom-node
const node = ReactDOM.findDOMNode(this) as Element
onEnter(node, mounting)
// Update the status to ENTERING, then update the status to ENTERED after the specified timeout interval
this.safeSetState({ status: ENTERING }, () = > {
onEntering(node, mounting)
this.onTransitionEnd(timeout, () = > {
this.safeSetState({ status: ENTERED }, () = > {
onEntered(node, mounting)
})
})
})
}
// Perform exit operations
performExit() {
// console.log(' Perform exit animation ')
const { onExit, onExiting, onExited, timeout } = this.props
const node = ReactDOM.findDOMNode(this) as Element
onExit(node)
// Update the status no longer intended to be withdraw, then update the status to EXITED after the specified timeout interval
this.safeSetState({ status: EXITING }, () = > {
onExiting(node)
this.onTransitionEnd(timeout, () = > {
this.safeSetState({ status: EXITED }, () = > {
onExited(node)
})
})
})
}
// Cancel asynchronously executed callback functions
cancelNextCallback() {
if (this.nextCallback && this.nextCallback.cancel) {
this.nextCallback.cancel()
}
}
// Update the status
updateStatus(mounting = false, nextStatus) {
if(nextStatus ! = =null) {
this.cancelNextCallback() // Cancel the last callback
if (nextStatus === ENTERING) {
this.performEnter(mounting) The mounting function controls whether the initial entry animation is displayed
} else {
this.performExit() // Perform exit animation related}}}render() {
const {
children,
onEnter: _onEnter,
onEntering: _onEntering,
onEntered: _onEntered,
onExit: _onExit,
onExiting: _onExiting,
onExited: _onExited,
in: _in,
timeout: _timeout, ... childProps } =this.props
const { status } = this.state
return (
<TransitionGroupContext.Provider value={null}>
{typeof children === 'function'
? children(status)
: React.cloneElement(
React.Children.only(children) as React.ReactElement,
childProps,
)}
</TransitionGroupContext.Provider>)}}Copy the code
The Transition component maintains a status state, which represents the different states of the component during the mount and unload phases. The initial state is first determined based on the in value passed in, and then status is passed to children at render. After the Transition component is initialized, when the in value passed to the Transition component changes, the encapsulated updateStatus method is called to update the internally maintained status value. If the status value to be updated is true, When the value of status to be updated is false, the exit animation calls the performExit method to update status.In order to solve the problem of frequently updating state in a short time and triggering the updated callback function multiple times, a safeSetState method is encapsulated here. The method receives two parameters, the first parameter is the state to be updated, and the second parameter is the callback function to be executed after the state update. The setNextCallback method is called inside the callback function, stored inside the setNextCallback method, and the closure method maintains a variable active that controls the execution of the callback method. CancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback cancelNextCallback This prevents the last stored callback from executing. So far, we have simply implemented a Transition component. The outside world can control the mounting and unloading of the component by controlling the IN value, and obtain different states during mounting and unloading, so as to achieve the animation customization of each state.
This place simply implements the various phases of the mount and unmount cycle of components, and does not address the different phases of the initial mount of components, as well as the uninstall and removal of components.
Implement a CSSTransition manually
2.1 Look at an example
import React, { useState } from 'react'
import CSSTransition from 'react-transition-group'
export default function CSSTransitionDemo () {
const [inProp, setInProp] = useState(false)
return (
<>
<CSSTransition
in={inProp}
timeout={2000}
classNames="my-node"
>
<div id="test">
{"I'll receive my-node-* classes"}
</div>
</CSSTransition>
<button type="button" onClick={()= >setInProp(! inProp)}> Click to Enter</button>
</>)}// the first time you click the button, you can see that the class name of the div#test tag has changed by reviewing the element.
// My-node-enter, my-node-enter my-node-enter >>>(2s later) my-node-enter-done
// when you click the button for the second time, you can see that the class name of the div#test tag has changed.
// "my-node-exit", "my-node-exit my-node-exit active" >>> (2s later) "my-node-exit-done"
Copy the code
When you first click the button, you can see that the class name of the div#test tag has changed by reviewing the element: “My-node-enter”, “my-node-enter my-node-enter-active” >>>(2s later) “my-node-enter-done” When you click the button for the second time, >> (2s later) > my-node-exit-done (2s later)
2.2 Core Principles
In the first section, we can see that the Transition component helps us manage the mount and unload cycles of components and exposes the following callback functions:
- Called when the onEnter approach animation starts execution
- OnEntering called in the execution of the entry animation
- OnEntered enters the call after the animation executes
- OnExit is called when the exit animation begins execution
- Onwithdraw exit animation is called during execution
- OnExited exit animation is called after execution
The CSSTransition component takes advantage of these exposed callback interfaces to help us add different state class names to child components at different stages.
2.3 Manually implement a simple version of CSSTransition
import React from 'react'
import Transition, { TransitionPropTypes } from './transition'
import { addClassNames, removeClassNames } from './utils'
interface CSSTransitionPropTypes extends TransitionPropTypes {
classNames: string
}
export default function CSSTransition(props: CSSTransitionPropTypes) {
const{ classNames, onEnter, onEntering, onEntered, onExit, onExiting, onExited, ... otherProps } = props// Return the base active done class name
const getClassNames = (status) = > {
const { classNames } = props
const baseClassName = `${classNames}-${status}`
const activeClassName = `${classNames}-${status}-active`
const doneClassName = `${classNames}-${status}-done`
return {
base: baseClassName,
active: activeClassName,
done: doneClassName,
}
}
// Add a class name to a given DOM node and control whether the browser forces a redraw
const addClassNamesAndForceRepaint = (
node,
classNames,
forceRepaint = false.) = > {
// The main purpose here is to force browser redraw
if (forceRepaint) {
node && node.offsetLeft
}
addClassNames(node, classNames)
}
// Remove the other class names and add the entry start class name
const _onEnter = (node, maybeAppear) = > {
// Remove the last class name
const exitClassNames = Object.values(getClassNames('exit'))
removeClassNames(node, exitClassNames)
// Add a new class name
const enterClassName = getClassNames('enter').base
addClassNamesAndForceRepaint(node, enterClassName)
if (onEnter) {
onEnter(node, maybeAppear)
}
}
// Add the class name of the approach progress
const _onEntering = (node, maybeAppear) = > {
// Add a new class name
const enteringClassName = getClassNames('enter').active
addClassNamesAndForceRepaint(node, enteringClassName, true)
// Execute the callback function
if (onEntering) {
onEntering(node, maybeAppear)
}
}
// Remove other class names and add end-of-approach class names
const _onEntered = (node, maybeAppear) = > {
// Remove the old class name
const enteringClassName = getClassNames('enter').active
const enterClassName = getClassNames('enter').base
removeClassNames(node, [enterClassName, enteringClassName])
// Add a new class name
const enteredClassName = getClassNames('enter').done
addClassNamesAndForceRepaint(node, enteredClassName)
// Execute the callback function
if (onEntered) {
onEntered(node, maybeAppear)
}
}
// Remove other class names and add exit start class names
const _onExit = (node) = > {
// Remove the last class name
const enteredClassNames = Object.values(getClassNames('enter'))
removeClassNames(node, enteredClassNames)
// Add a new class name
const exitClassName = getClassNames('exit').base
addClassNamesAndForceRepaint(node, exitClassName)
if (onExit) {
onExit(node)
}
}
// Add the name of the exit ongoing class
const _onExiting = (node) = > {
const exitingClassName = getClassNames('exit').active
addClassNamesAndForceRepaint(node, exitingClassName, true)
if (onExit) {
onExit(node)
}
}
// Add the exit completion class name
const _onExited = (node) = > {
const exitingClassName = getClassNames('exit').active
const exitClassName = getClassNames('exit').base
removeClassNames(node, [exitClassName, exitingClassName])
const exitedClassName = getClassNames('exit').done
addClassNamesAndForceRepaint(node, exitedClassName)
if (onExited) {
onExited(node)
}
}
return (
<Transition
{. otherProps}
onEnter={_onEnter}
onEntering={_onEntering}
onEntered={_onEntered}
onExit={_onExit}
onExiting={_onExiting}
onExited={_onExited}
>
{props.children}
</Transition>)}Copy the code
Nothing complicated about this part is that it simply wraps the onEnter, onEntering, onEntered, onExit, onwithdraw, onExited callback functions passed to the Transition component, The encapsulated content is to add different state class names to children at different stages, and then pass the encapsulated callback data to the Transition component. This allows CSSTransition to add different state class names to our children component at different stages, and we can set different styles for different state class names to animate them.
Manually implement a SwitchTransition
The main purpose of this component is to animate the switch between two components.
3.1 Core Principles
SwitchTransition provides two switching modes: out-in and in-out. The differences between the two switching modes are shown as follows:
As shown in the video above, in out-of-in mode, when the two components switch, the entry animation of the other component will be triggered after the last component leaves the field. In-out mode performs the entry animation of the next component and then the departure animation of the previous component when switching between the two components. How out-of-in mode works: In this mode, when switching components, the entry of one component will be performed after the exit of another component. React A component’s key changes, causing the component to be remounted. When switching components, change the key. SwitchTransition monitors whether the last component mounted (named component A) is the same as the latest component mounted (named component B). If not, instead of rendering the current component B, continue rendering the previous component (A) and trigger the exit animation of the previous component (A). After the exit animation is executed, start rendering the updated component (B) and trigger the entry animation. At this point, the two components switch to achieve the exit animation and the entry animation.How in-out mode works: When switching components, the next component enters and the last component exits. When switching components A and B (i.e., changing the key value of the component), the SwitchTransition intercepts and does not render the latest component B. Instead, the SwitchTransition renders both components A and B and triggers the entry animation of component B. After the completion of the entry animation of component B, the departure animation of component A is triggered. So far, the switching animation of the two components, advanced field and backward departure, has been completed.
3.2 Manually implement a simple version of SwitchTransition
// Helper class utility functions
const callHook =
(element, name, cb) = >
(. args) = >{ element.props[name] && element.props[name](... args) cb() }// Store offsite related render components
const leaveRenders = {
[modes.out]: ({ current, changeState }) = >
React.cloneElement(current, {
in: false.onExited: callHook(current, 'onExited'.() = > {
changeState(ENTERING, null)})}),// 'in-out' mode mounts both incoming and outgoing components
[modes.in]: ({ current, changeState, children }) = > [
current,
React.cloneElement(children, {
in: true.onEntered: callHook(children, 'onEntered'.() = > {
changeState(ENTERING)
}),
}),
],
}
// Store the render components associated with the approach
const enterRenders = {
[modes.out]: ({ children, changeState }) = >
React.cloneElement(children, {
in: true.onEntered: callHook(children, 'onEntered'.() = > {
changeState(ENTERED, React.cloneElement(children, { in: true})})}),// 'in-out' mode mounts both incoming and outgoing components
[modes.in]: ({ current, children, changeState }) = > [
React.cloneElement(current, {
in: false.onExited: callHook(current, 'onExited'.() = > {
changeState(ENTERED, React.cloneElement(children, { in: true }))
}),
}),
React.cloneElement(children, {
in: true,})],}Copy the code
interface IProps { children? : React.ReactNode |undefinedmode? :'out-in' | 'in-out'
}
interface IState {
status: string
current: React.ReactNode | null
}
export default class SwitchTransition extends Component<IProps.IState> {
constructor(props) {
super(props)
this.state = {
status: ENTERED,
current: null,}}// It is used to control the first time a child component is mounted to perform the approach animation
private isMounted = false
static getDerivedStateFromProps(props, state) {
if (props.children == null) {
return {
current: null,}}if (state.status === ENTERING && props.mode === modes.in) {
return {
status: ENTERING,
}
}
// Current has a value and children have changed
if (state.current && areChildrenDifferent(state.current, props.children)) {
return {
status: EXITING,
}
}
return {
current: React.cloneElement(props.children, { in: true}}}),componentDidMount() {
this.isMounted = true
}
changeState(status, current = this.state.current) {
this.setState({ status, current })
}
render() {
const {
state: { status, current },
props: { children, mode = 'out-in'}} =this
const data = { children, current, changeState: this.changeState.bind(this)}let component
switch (status) {
case ENTERING:
component = enterRenders[mode](data) // Mount the approach component
break
case EXITING:
component = leaveRenders[mode](data) // Mount the off-field component
break
case ENTERED:
component = current
break
}
return (
<TransitionGroupContext.Provider// The main purpose here is to solve the problem of the child component performing an approach animation after the first mountvalue={{ status: this.isMounted ? ENTERING : ENTERED }}
>
{component}
</TransitionGroupContext.Provider>)}}Copy the code
The following is a brief flow diagram of the SwitchTransition execution, when it is first mounted, storing children under current in the state in the lifecycle function getDerivedStateFromProps. Then take it out and render it in render. When switching components, for example, from component A to component B, the children of the SwitchTransition component changes, so the periodic function getDerivedStateFromProps is triggered. Internally altering the status value no longer intended to be part of the coinage, the render will be rendered differently based on the status and mode values. When it is in in-out mode, leaveRenders will be taken out at this time’in-out’Component renders, that is, renders A and triggers the exit animation for A. When the exit animation finishes, change status to ENTERING renders and enterRenders’in-out’Component renders, that is, the B component, and triggers its approach animation. When the approach animation is finished, the in-out mode switch is completed. When it is in out-of-in mode, leaveRenders will be taken out at this time’out-in’Component renders, i.e., renders A and B simultaneously and triggers the approach animation for component B. After the approach animation for component B is completed, it changes status to ENTERING renders and enterRenders’out- in’Components render, that is, render components A and B at the same time and trigger the departure animation of component A. After the execution of the departure animation, an out-of-in mode switch is completed.At this point, you have implemented a simple version of the SwitchTransition component that can animate entry and exit when two components switch.
Implement a TransitionGroup manually
4.1 Look at an example
import React from 'react';
// import { CSSTransition, TransitionGroup } from 'react-transition-group';
import {
CSSTransition,
TransitionGroup,
} from '@/pages/react-transition-group/min-react-transition-group';
import './index.less';
export default class TodoList extends React.Component<
{},
{ items: Array<string> }
> {
count: number = 1;
constructor(props) {
super(props);
this.state = { items: ['hello'.'world'.'click'.'me']}; }handleAdd() {
const newItems = this.state.items.concat([`item-The ${this.count++}`]);
this.setState({ items: newItems });
}
handleRemove(i) {
const newItems = this.state.items.slice();
newItems.splice(i, 1);
this.setState({ items: newItems });
}
render() {
return (
<div>
<button onClick={()= > this.handleAdd()}>Add Item</button>
<TransitionGroup>
{this.state.items.map((item, i) => (
<CSSTransition key={item} timeout={2000} classNames="friend">
<div>
{item}
<button onClick={()= > this.handleRemove(i)}>remove</button>
</div>
</CSSTransition>
))}
</TransitionGroup>
</div>); }}Copy the code
.friend-enter {
transform: translate(100%.0);
opacity: 0;
}
.friend-enter-active {
transform: translate(0.0);
opacity: 1;
transition: all 500ms;
}
.friend-exit {
transform: translate(0.0);
opacity: 1;
}
.friend-exit-active {
transform: translate(-100%.0);
opacity: 0;
transition: all 500ms;
}
Copy the code
As shown in the previous video, motion effects for adding or removing elements are implemented through TransitionGroup and CSSTransition.
4.2 Core Principles
This component is used to animate the entry and exit of a set of elements. For example, if we have a TodoList and we want to implement an animation that adds a Todo or removes a Todo, we can use this component to implement it. The core principle is to store the children rendered last time, and then compare the children rendered this time to judge the changes of children and determine whether the child is added or deleted. When the child is added, the opening animation is injected into the child element added this time. If the child is reduced, the child element is not unloaded directly. Instead, an exit animation is injected into the child element, and the element to be reduced is unloaded after the exit animation is completed.
4.3 Manually implement a simple version of TransitionGroup
Function getChildrenMapping(children, mapFn = (c) => c) {const result =Object.create(null);
React.Children.forEach(children, (c) => {
result[c.key]= mapFn(c); }); return result; } // Incorporate the last state and the next state childrenMapping, ensuring that the returned mappings include all the child function mergeChildMappings(prev, next) { This is simplified, except that a mapping that holds all children returns const prevNum =Object.keys(prev).length;
const nextNum = Object.keys(next).length; return prevNum > nextNum ? prev : next; } / / get the initial state of chidlrenMapping function getInitialChildrenMapping (children) {return getChildrenMapping (children, (c) => { return React.cloneElement(c, { in: true, }); }); } // getNextChildrenMapping function getNextChildrenMapping(nextProps, prevChildrenMapping, handleExited) { const result =Object.create(null); // Children const {children: nextChildren} = nextProps; Const nextChildrenMapping = getChildrenMapping(nextChildren); const nextChildrenMapping = getChildrenMapping(nextChildren); Const mergeMappings = mergeChildMappings(prevChildrenMapping, nextChildrenMapping,);Object.keys(mergeMappings).forEach((key) => { const isNext = key in nextChildrenMapping; const isPrev = key in prevChildrenMapping; // Add element if (isNext &&! isPrev) { result[key] = React.cloneElement(nextChildrenMapping[key], {in: true, // set entry state}); } // Delete the element if (! isNext && isPrev) { // debugger; result[key] = React.cloneElement(prevChildrenMapping[key], {in: false,// Sets the off-field stateonExited() {// Uninstall the current element after the exit animation completeshandleExited(prevChildrenMapping[key]); }}); } / / first mount | | unchanged for the elements of the if (isNext && isPrev) {result[key] = React.cloneElement(nextChildrenMapping[key], { in: true, }); }}); return result; }Copy the code
import React from 'react';
import { TransitionGroupContext } from/ '.transition-context';
import { ENTERING, ENTERED } from/ '.transition'; interface TransitionGroupPropTypes { children? : React.ReactElement | Array<React.ReactElement>; } export default class TransitionGroup extends React.Component< TransitionGroupPropTypes, { children: Object; // key-child status: string; // Control first mount animation firstRender: Boolean; handleExited: (child, node) => void; } > {constructor(props) {super(props); const handleExited = this.handleExited.bind(this);
this.state = {
children: {},
status: ENTERED,
handleExited,
firstRender: true,
};
}
static getDerivedStateFromProps(
nextProps,
{ children, firstRender, handleExited },
) {
return {
children: firstRender
? getInitialChildrenMapping(nextProps.children)
: getNextChildrenMapping(nextProps, children, handleExited),
firstRender: false,
};
}
componentDidMount() {
this.setState({ status: ENTERING, }); } handleExited(child, node) {// Remove child this from children.setState((state) => {
const children = { ...state.children };
delete children[child.key];
return {
children,
};
});
}
render() {
const { children, status } = this.state;
const component = Object.values(children);
return (
<TransitionGroupContext.Provider value={{ status }}>
{component}
</TransitionGroupContext.Provider>); }}Copy the code
First determine the execution sequence of the lifecycle functions used in this paper. Constructor >> getDerivedStateFromProps >> Render >> componentDidMount is executed when the TransitionGroup component is first mounted The lifecycle execution order for each subsequent update to the props value is getDerivedStateFromProps >> Render.
When the TransitionGroup component is mounted for the first time, the constructor constructor is executed, in which some parameters are initialized, and the getDerivedStateFromProps cycle function is executed. There to call getInitialChildrenMapping method to realize map the children as a mapping, in which the key value of the mapping for each child’s key, the value value for the child itself. Const Component = Object.values(children); const component = object. values(children); Component components.
When the children of TransitionGroup changes, it is intercepted by the getDerivedStateFromProps declaration cycle function to complete the refactoring of the next rendering component. Specific things to do are:
- When a new element is detected, an in = true attribute is added to the new element, triggering its approach animation.
- When an element is detected to be deleted, children after the element is deleted will not be rendered directly. Instead, in = false attribute will be added to the deleted element to trigger its exit animation. After the execution of the exit animation, children after the deleted element will be mounted.
conclusion
Thank you for your patience! Now to sum up! The react-transition-group library itself does not help us implement any kind of animation, but it does manage the stages of mounting and unmounting components based on the Transition component. It also provides CSSTransition on the basis of Transition, so that we can use CSS style to manage different stages of rendering, so as to achieve animation. In addition, we also provide SwitchTransition, TransitionGroup components, so that we can switch between two or more components of the animation.