Skip to content

Commit

Permalink
chore(TransitionGroup): remove deprecated lifecycle methods (#3970)
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter committed Jun 26, 2020
1 parent bd92e01 commit 1d83ac1
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 62 deletions.
1 change: 0 additions & 1 deletion src/lib/index.js
Expand Up @@ -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 {
Expand Down
113 changes: 53 additions & 60 deletions 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')

Expand Down Expand Up @@ -43,46 +37,72 @@ 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
}

// Heads up!
// 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
}

Expand All @@ -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 (
<Transition
animation={animation}
directional={directional}
duration={duration}
key={key}
onHide={this.handleOnHide}
reactKey={key}
transitionOnMount={transitionOnMount}
visible={visible}
>
{child}
</Transition>
)
return { children }
}

render() {
Expand Down
File renamed without changes.
34 changes: 34 additions & 0 deletions 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 (
<Transition
animation={animation}
directional={directional}
duration={duration}
key={key}
onHide={onHide}
reactKey={key}
transitionOnMount={transitionOnMount}
visible={visible}
>
{child}
</Transition>
)
}
@@ -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', () => {
Expand Down

0 comments on commit 1d83ac1

Please sign in to comment.