From 1d83ac1b5e3f73887507d1368e4bae7cf552e531 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Fri, 26 Jun 2020 10:00:03 +0200 Subject: [PATCH] chore(TransitionGroup): remove deprecated lifecycle methods (#3970) --- src/lib/index.js | 1 - src/modules/Transition/TransitionGroup.js | 113 ++++++++---------- .../Transition/utils}/childMapping.js | 0 src/modules/Transition/utils/wrapChild.js | 34 ++++++ .../Transition/utils}/childMapping-test.js | 2 +- 5 files changed, 88 insertions(+), 62 deletions(-) rename src/{lib => modules/Transition/utils}/childMapping.js (100%) create mode 100644 src/modules/Transition/utils/wrapChild.js rename test/specs/{lib => modules/Transition/utils}/childMapping-test.js (96%) diff --git a/src/lib/index.js b/src/lib/index.js index cfefc62a28..20c79369ff 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -2,7 +2,6 @@ import makeDebugger from './makeDebugger' export AutoControlledComponent from './AutoControlledComponent' export ModernAutoControlledComponent from './ModernAutoControlledComponent' -export { getChildMapping, mergeChildMappings } from './childMapping' export * as childrenUtils from './childrenUtils' export { diff --git a/src/modules/Transition/TransitionGroup.js b/src/modules/Transition/TransitionGroup.js index bb3e5c7014..70a9f32ce4 100644 --- a/src/modules/Transition/TransitionGroup.js +++ b/src/modules/Transition/TransitionGroup.js @@ -1,16 +1,10 @@ import _ from 'lodash' import PropTypes from 'prop-types' -import React, { cloneElement, Fragment } from 'react' - -import { - getChildMapping, - getElementType, - getUnhandledProps, - makeDebugger, - mergeChildMappings, - SUI, -} from '../../lib' -import Transition from './Transition' +import React from 'react' + +import { getElementType, getUnhandledProps, makeDebugger, SUI } from '../../lib' +import { getChildMapping, mergeChildMappings } from './utils/childMapping' +import wrapChild from './utils/wrapChild' const debug = makeDebugger('transition_group') @@ -43,38 +37,64 @@ export default class TransitionGroup extends React.Component { } static defaultProps = { - as: Fragment, + as: React.Fragment, animation: 'fade', duration: 500, } - constructor(...args) { - super(...args) + state = { + // Keeping a callback under the state is a hack to make it accessible under getDerivedStateFromProps() + handleOnHide: (nothing, childProps) => { + debug('handleOnHide', childProps) + const { reactKey } = childProps - const { children } = this.props - this.state = { - children: _.mapValues(getChildMapping(children), (child) => this.wrapChild(child)), - } + this.setState((state) => { + const children = { ...state.children } + delete children[reactKey] + + return { children } + }) + }, } - // eslint-disable-next-line camelcase - UNSAFE_componentWillReceiveProps(nextProps) { - debug('componentWillReceiveProps()') + static getDerivedStateFromProps(props, state) { + debug('getDerivedStateFromProps()') + + const { animation, duration, directional } = props + const { children: prevMapping } = state + + // A short circuit for an initial render as there will be no `prevMapping` + if (typeof prevMapping === 'undefined') { + return { + children: _.mapValues(getChildMapping(props.children), (child) => + wrapChild(child, state.handleOnHide, { + animation, + duration, + directional, + }), + ), + } + } - const { children: prevMapping } = this.state - const nextMapping = getChildMapping(nextProps.children) + const nextMapping = getChildMapping(props.children) const children = mergeChildMappings(prevMapping, nextMapping) _.forEach(children, (child, key) => { const hasPrev = _.has(prevMapping, key) const hasNext = _.has(nextMapping, key) + const { [key]: prevChild } = prevMapping const isLeaving = !_.get(prevChild, 'props.visible') // Heads up! // An item is new (entering), it will be picked from `nextChildren`, so it should be wrapped if (hasNext && (!hasPrev || isLeaving)) { - children[key] = this.wrapChild(child, { transitionOnMount: true }) + children[key] = wrapChild(child, state.handleOnHide, { + animation, + duration, + directional, + transitionOnMount: true, + }) return } @@ -82,7 +102,7 @@ export default class TransitionGroup extends React.Component { // An item is old (exiting), it will be picked from `prevChildren`, so it has been already // wrapped, so should be only updated if (!hasNext && hasPrev && !isLeaving) { - children[key] = cloneElement(prevChild, { visible: false }) + children[key] = React.cloneElement(prevChild, { visible: false }) return } @@ -93,43 +113,16 @@ export default class TransitionGroup extends React.Component { props: { visible, transitionOnMount }, } = prevChild - children[key] = this.wrapChild(child, { transitionOnMount, visible }) + children[key] = wrapChild(child, state.handleOnHide, { + animation, + duration, + directional, + transitionOnMount, + visible, + }) }) - this.setState({ children }) - } - - handleOnHide = (nothing, childProps) => { - debug('handleOnHide', childProps) - const { reactKey } = childProps - - this.setState((state) => { - const children = { ...state.children } - delete children[reactKey] - - return { children } - }) - } - - wrapChild = (child, options = {}) => { - const { animation, directional, duration } = this.props - const { key } = child - const { visible = true, transitionOnMount = false } = options - - return ( - - {child} - - ) + return { children } } render() { diff --git a/src/lib/childMapping.js b/src/modules/Transition/utils/childMapping.js similarity index 100% rename from src/lib/childMapping.js rename to src/modules/Transition/utils/childMapping.js diff --git a/src/modules/Transition/utils/wrapChild.js b/src/modules/Transition/utils/wrapChild.js new file mode 100644 index 0000000000..c07eacb975 --- /dev/null +++ b/src/modules/Transition/utils/wrapChild.js @@ -0,0 +1,34 @@ +import React from 'react' +import Transition from '../Transition' + +/** + * Wraps a React element with a Transition component. + * + * @param {React.ReactElement} child + * @param {Function} onHide + * @param {Object} [options={}] + * @param {String} [options.animation] + * @param {Number} [options.duration] + * @param {Boolean} [options.directional] + * @param {Boolean} [options.transitionOnMount=false] + * @param {Boolean} [options.visible=true] + */ +export default function wrapChild(child, onHide, options = {}) { + const { key } = child + const { animation, directional, duration, transitionOnMount = false, visible = true } = options + + return ( + + {child} + + ) +} diff --git a/test/specs/lib/childMapping-test.js b/test/specs/modules/Transition/utils/childMapping-test.js similarity index 96% rename from test/specs/lib/childMapping-test.js rename to test/specs/modules/Transition/utils/childMapping-test.js index 58aa04820b..68599f3def 100644 --- a/test/specs/lib/childMapping-test.js +++ b/test/specs/modules/Transition/utils/childMapping-test.js @@ -1,5 +1,5 @@ import React from 'react' -import { getChildMapping, mergeChildMappings } from 'src/lib' +import { getChildMapping, mergeChildMappings } from 'src/modules/Transition/utils/childMapping' describe('childMapping', () => { describe('childMapping', () => {