@@ -5,12 +5,25 @@ import { cloneElement, Component } from 'react'
55
66import { makeDebugger , normalizeTransitionDuration , SUI , useKeyOnly } from '../../lib'
77import TransitionGroup from './TransitionGroup'
8+ import {
9+ computeStatuses ,
10+ TRANSITION_STATUS_ENTERED ,
11+ TRANSITION_STATUS_ENTERING ,
12+ TRANSITION_STATUS_EXITED ,
13+ TRANSITION_STATUS_EXITING ,
14+ TRANSITION_STATUS_INITIAL ,
15+ TRANSITION_STATUS_UNMOUNTED ,
16+ } from './utils/computeStatuses'
817
918const debug = makeDebugger ( 'transition' )
1019
11- const TRANSITION_TYPE = {
12- ENTERING : 'show' ,
13- EXITING : 'hide' ,
20+ const TRANSITION_CALLBACK_TYPE = {
21+ [ TRANSITION_STATUS_ENTERED ] : 'show' ,
22+ [ TRANSITION_STATUS_EXITED ] : 'hide' ,
23+ }
24+ const TRANSITION_STYLE_TYPE = {
25+ [ TRANSITION_STATUS_ENTERING ] : 'show' ,
26+ [ TRANSITION_STATUS_EXITING ] : 'hide' ,
1427}
1528
1629/**
@@ -94,96 +107,84 @@ export default class Transition extends Component {
94107 unmountOnHide : false ,
95108 }
96109
97- static ENTERED = 'ENTERED'
98- static ENTERING = 'ENTERING'
99- static EXITED = 'EXITED'
100- static EXITING = 'EXITING'
101- static UNMOUNTED = 'UNMOUNTED'
110+ /** @deprecated Static properties will be removed in v1 */
111+ static INITIAL = TRANSITION_STATUS_INITIAL
112+ static ENTERED = TRANSITION_STATUS_ENTERED
113+ static ENTERING = TRANSITION_STATUS_ENTERING
114+ static EXITED = TRANSITION_STATUS_EXITED
115+ static EXITING = TRANSITION_STATUS_EXITING
116+ static UNMOUNTED = TRANSITION_STATUS_UNMOUNTED
102117
103118 static Group = TransitionGroup
104119
105- constructor ( ...args ) {
106- super ( ...args )
107-
108- const { initial : status , next } = this . computeInitialStatuses ( )
109- this . nextStatus = next
110- this . state = { status }
120+ state = {
121+ status : TRANSITION_STATUS_INITIAL ,
111122 }
112123
113124 // ----------------------------------------
114125 // Lifecycle
115126 // ----------------------------------------
116127
117- componentDidMount ( ) {
118- debug ( 'componentDidMount()' )
119-
120- this . updateStatus ( )
121- }
128+ static getDerivedStateFromProps ( props , state ) {
129+ const derivedState = computeStatuses ( {
130+ mountOnShow : props . mountOnShow ,
131+ status : state . status ,
132+ transitionOnMount : props . transitionOnMount ,
133+ visible : props . visible ,
134+ unmountOnHide : props . unmountOnHide ,
135+ } )
122136
123- // eslint-disable-next-line camelcase
124- UNSAFE_componentWillReceiveProps ( nextProps ) {
125- debug ( 'componentWillReceiveProps()' )
137+ debug ( 'getDerivedStateFromProps()' , props , state , derivedState )
126138
127- const { current : status , next } = this . computeStatuses ( nextProps )
139+ return derivedState
140+ }
128141
129- this . nextStatus = next
130- if ( status ) this . setState ( { status } )
142+ componentDidMount ( ) {
143+ debug ( 'componentDidMount()' )
144+ this . updateStatus ( { } )
131145 }
132146
133- componentDidUpdate ( ) {
147+ componentDidUpdate ( prevProps , prevState ) {
134148 debug ( 'componentDidUpdate()' )
135-
136- this . updateStatus ( )
149+ this . updateStatus ( prevState )
137150 }
138151
139152 componentWillUnmount ( ) {
140153 debug ( 'componentWillUnmount()' )
141-
142154 clearTimeout ( this . timeoutId )
143155 }
144156
145157 // ----------------------------------------
146158 // Callback handling
147159 // ----------------------------------------
148160
149- handleStart = ( ) => {
161+ handleStart = ( nextStatus ) => {
150162 const { duration } = this . props
151- const status = this . nextStatus
152163
153- this . nextStatus = null
154- this . setState ( { status, animating : true } , ( ) => {
155- const durationType = TRANSITION_TYPE [ status ]
156- const durationValue = normalizeTransitionDuration ( duration , durationType )
164+ const durationType = TRANSITION_CALLBACK_TYPE [ nextStatus ]
165+ const durationValue = normalizeTransitionDuration ( duration , durationType )
157166
158- _ . invoke ( this . props , 'onStart' , null , { ...this . props , status } )
159- this . timeoutId = setTimeout ( this . handleComplete , durationValue )
160- } )
167+ clearTimeout ( this . timeoutId )
168+ this . timeoutId = setTimeout (
169+ ( ) => this . setState ( ( state ) => ( { status : state . nextStatus } ) ) ,
170+ durationValue ,
171+ )
161172 }
162173
163- handleComplete = ( ) => {
164- const { status : current } = this . state
165-
166- _ . invoke ( this . props , 'onComplete' , null , { ...this . props , status : current } )
167-
168- if ( this . nextStatus ) {
169- this . handleStart ( )
170- return
174+ updateStatus = ( prevState ) => {
175+ if ( this . state . status !== this . state . nextStatus && this . state . nextStatus ) {
176+ this . handleStart ( this . state . nextStatus )
171177 }
172178
173- const status = this . computeCompletedStatus ( )
174- const callback = current === Transition . ENTERING ? 'onShow' : 'onHide'
175-
176- this . setState ( { status, animating : false } , ( ) => {
177- _ . invoke ( this . props , callback , null , { ...this . props , status } )
178- } )
179- }
179+ if ( ! prevState . animating && this . state . animating ) {
180+ _ . invoke ( this . props , 'onStart' , null , { ...this . props , status : this . state . status } )
181+ }
180182
181- updateStatus = ( ) => {
182- const { animating } = this . state
183+ if ( prevState . animating && ! this . state . animating ) {
184+ const callback = this . state . status === TRANSITION_STATUS_ENTERED ? 'onShow' : 'onHide'
183185
184- if ( this . nextStatus ) {
185- this . nextStatus = this . computeNextStatus ( )
186- if ( ! animating ) this . handleStart ( )
186+ _ . invoke ( this . props , 'onComplete' , null , { ...this . props , status : this . state . status } )
187+ _ . invoke ( this . props , callback , null , { ...this . props , status : this . state . status } )
187188 }
188189 }
189190
@@ -205,72 +206,23 @@ export default class Transition extends Component {
205206 animation ,
206207 childClasses ,
207208 useKeyOnly ( animating , 'animating' ) ,
208- useKeyOnly ( status === Transition . ENTERING , 'in' ) ,
209- useKeyOnly ( status === Transition . EXITING , 'out' ) ,
210- useKeyOnly ( status === Transition . EXITED , 'hidden' ) ,
211- useKeyOnly ( status !== Transition . EXITED , 'visible' ) ,
209+ useKeyOnly ( status === TRANSITION_STATUS_ENTERING , 'in' ) ,
210+ useKeyOnly ( status === TRANSITION_STATUS_EXITING , 'out' ) ,
211+ useKeyOnly ( status === TRANSITION_STATUS_EXITED , 'hidden' ) ,
212+ useKeyOnly ( status !== TRANSITION_STATUS_EXITED , 'visible' ) ,
212213 'transition' ,
213214 )
214215 }
215216
216217 return cx ( animation , childClasses , useKeyOnly ( animating , 'animating transition' ) )
217218 }
218219
219- computeCompletedStatus = ( ) => {
220- const { unmountOnHide } = this . props
221- const { status } = this . state
222-
223- if ( status === Transition . ENTERING ) return Transition . ENTERED
224- return unmountOnHide ? Transition . UNMOUNTED : Transition . EXITED
225- }
226-
227- computeInitialStatuses = ( ) => {
228- const { visible, mountOnShow, transitionOnMount, unmountOnHide } = this . props
229-
230- if ( visible ) {
231- if ( transitionOnMount ) {
232- return {
233- initial : Transition . EXITED ,
234- next : Transition . ENTERING ,
235- }
236- }
237- return { initial : Transition . ENTERED }
238- }
239-
240- if ( mountOnShow || unmountOnHide ) return { initial : Transition . UNMOUNTED }
241- return { initial : Transition . EXITED }
242- }
243-
244- computeNextStatus = ( ) => {
245- const { animating, status } = this . state
246-
247- if ( animating ) return status === Transition . ENTERING ? Transition . EXITING : Transition . ENTERING
248- return status === Transition . ENTERED ? Transition . EXITING : Transition . ENTERING
249- }
250-
251- computeStatuses = ( props ) => {
252- const { status } = this . state
253- const { visible } = props
254-
255- if ( visible ) {
256- return {
257- current : status === Transition . UNMOUNTED && Transition . EXITED ,
258- next :
259- status !== Transition . ENTERING && status !== Transition . ENTERED && Transition . ENTERING ,
260- }
261- }
262-
263- return {
264- next : ( status === Transition . ENTERING || status === Transition . ENTERED ) && Transition . EXITING ,
265- }
266- }
267-
268220 computeStyle = ( ) => {
269221 const { children, duration } = this . props
270222 const { status } = this . state
271223
272224 const childStyle = _ . get ( children , 'props.style' )
273- const type = TRANSITION_TYPE [ status ]
225+ const type = TRANSITION_STYLE_TYPE [ status ]
274226 const animationDuration = type && `${ normalizeTransitionDuration ( duration , type ) } ms`
275227
276228 return { ...childStyle , animationDuration }
@@ -281,14 +233,16 @@ export default class Transition extends Component {
281233 // ----------------------------------------
282234
283235 render ( ) {
284- debug ( 'render()' )
285- debug ( 'props' , this . props )
286- debug ( 'state' , this . state )
236+ debug ( 'render(): props' , this . props )
237+ debug ( 'render(): state' , this . state )
287238
288239 const { children } = this . props
289240 const { status } = this . state
290241
291- if ( status === Transition . UNMOUNTED ) return null
242+ if ( status === TRANSITION_STATUS_UNMOUNTED ) {
243+ return null
244+ }
245+
292246 return cloneElement ( children , {
293247 className : this . computeClasses ( ) ,
294248 style : this . computeStyle ( ) ,
0 commit comments