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', () => {