Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Commit c09ac18

Browse files
committed
Improved state handling
1 parent d41a5a8 commit c09ac18

File tree

9 files changed

+532
-496
lines changed

9 files changed

+532
-496
lines changed

src/csstransition.tsx

Lines changed: 22 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
/**
22
* @license
3-
* Copyright (C) 2016 Chi Vinh Le and contributors.
3+
* Copyright (C) 2016-present Chi Vinh Le and contributors.
44
*
55
* This software may be modified and distributed under the terms
66
* of the MIT license. See the LICENSE file for details.
77
*/
88

9-
/* tslint:disable: variable-name no-switch-case-fall-through */
10-
119
import * as React from "react";
1210
import {
1311
CSSProperties, Component, ComponentClass, ReactNode,
1412
StatelessComponent, ReactElement, HTMLAttributes,
1513
} from "react";
1614

17-
import { resolveTransit } from "./transit";
15+
import { ActionID, CSSTransitionState, reduce } from "./csstransitionstate";
1816
import { TransitionObserver } from "./transitionobserver";
19-
import { TransitionDelay, getEnterDelay, getLeaveDelay } from "./utils";
20-
21-
const TICK = 17;
17+
import { TransitionDelay, runInFrame } from "./utils";
2218

2319
export type CSSTransitionDelay = TransitionDelay;
2420
export type CSSTransitionEventHandler = () => void;
@@ -44,95 +40,31 @@ export interface CSSTransitionProps
4440
// leaveInitStyle?: CSSProperties;
4541
}
4642

47-
export interface CSSTransitionState {
48-
id?: StateID;
49-
style?: CSSProperties;
50-
}
51-
52-
export enum StateID {
53-
Active,
54-
TransitToDefaultRunning,
55-
TransitToDefaultStarted,
56-
TransitToActiveAppearing,
57-
TransitToActiveRunning,
58-
TransitToActiveStarted,
59-
Default,
60-
}
61-
62-
enum Action {
63-
InitActive,
64-
InitDefault,
65-
TransitionAppear,
66-
TransitionSkip,
67-
TransitionRun,
68-
TransitionStart,
69-
TransitionComplete,
70-
}
71-
72-
const activeState = (props: CSSTransitionProps) => ({
73-
id: StateID.Active,
74-
style: { ...props.style, ...props.activeStyle },
75-
});
76-
77-
const defaultState = (props: CSSTransitionProps) => ({
78-
id: StateID.Default,
79-
style: { ...props.style, ...props.defaultStyle },
80-
});
81-
82-
const transitToActiveAppearingState = (props: CSSTransitionProps) => ({
83-
id: StateID.TransitToActiveAppearing,
84-
style: { ...props.style, ...props.defaultStyle },
85-
});
86-
87-
const transitToActiveRunningState = (props: CSSTransitionProps) => ({
88-
id: StateID.TransitToActiveRunning,
89-
style: { ...props.style, ...resolveTransit(props.enterStyle, getEnterDelay(props.transitionDelay)) },
90-
});
91-
92-
const transitToActiveStartedState = (props: CSSTransitionProps) => ({
93-
id: StateID.TransitToActiveStarted,
94-
style: { ...props.style, ...resolveTransit(props.enterStyle, getEnterDelay(props.transitionDelay)) },
95-
});
96-
97-
const transitToDefaultRunningState = (props: CSSTransitionProps) => ({
98-
id: StateID.TransitToDefaultRunning,
99-
style: { ...props.style, ...resolveTransit(props.leaveStyle, getLeaveDelay(props.transitionDelay)) },
100-
});
101-
102-
const transitToDefaultStartedState = (props: CSSTransitionProps) => ({
103-
id: StateID.TransitToDefaultStarted,
104-
style: { ...props.style, ...resolveTransit(props.leaveStyle, getLeaveDelay(props.transitionDelay)) },
105-
});
106-
10743
export class CSSTransition extends Component<CSSTransitionProps, CSSTransitionState> {
10844
public static defaultProps: any = {
10945
component: "div",
11046
};
11147

112-
private appearTimer: any;
48+
private cancelPending: any;
11349

11450
constructor(props: CSSTransitionProps) {
11551
super(props);
116-
let stateAction = Action.InitDefault;
117-
if (!props.transitionAppear && props.active) {
118-
stateAction = Action.InitActive;
119-
}
120-
this.dispatch(stateAction, props);
52+
this.dispatch(ActionID.Init, props);
12153
}
12254

12355
public componentDidMount(): void {
12456
if (this.props.transitionAppear && this.props.active) {
125-
this.dispatch(Action.TransitionAppear);
57+
this.dispatch(ActionID.TransitionTrigger);
12658
}
12759
}
12860

12961
public componentWillUnmount(): void {
130-
clearTimeout(this.appearTimer);
62+
if (this.cancelPending) { this.cancelPending(); this.cancelPending = null; }
13163
}
13264

13365
public componentWillReceiveProps(nextProps: CSSTransitionProps): void {
13466
if (this.props.active === nextProps.active) { return; }
135-
this.dispatch(Action.TransitionRun, nextProps);
67+
this.dispatch(ActionID.TransitionTrigger, nextProps);
13668
}
13769

13870
public render(): ReactElement<any> {
@@ -165,77 +97,20 @@ export class CSSTransition extends Component<CSSTransitionProps, CSSTransitionSt
16597
);
16698
}
16799

168-
private handleTransitionComplete = () => this.dispatch(Action.TransitionComplete);
169-
private handleTransitionBegin = () => this.dispatch(Action.TransitionStart);
170-
171-
private dispatch(action: Action, props = this.props, state = this.state): void {
172-
switch (action) {
173-
case Action.InitActive:
174-
if (state !== undefined) { throw new Error("invalid state transition"); }
175-
this.state = activeState(props);
176-
return;
177-
case Action.InitDefault:
178-
if (state !== undefined) { throw new Error("invalid state transition"); }
179-
this.state = defaultState(props);
180-
return;
181-
case Action.TransitionAppear:
182-
if (state.id !== StateID.Default) {
183-
throw new Error("invalid state transition");
184-
}
185-
this.appearTimer = setTimeout(() => {
186-
this.dispatch(Action.TransitionRun, props);
187-
}, TICK);
188-
return this.setState(transitToActiveAppearingState(props));
189-
case Action.TransitionRun:
190-
switch (state.id) {
191-
case StateID.TransitToActiveAppearing:
192-
clearTimeout(this.appearTimer);
193-
if (props.active) {
194-
return this.setState(transitToActiveRunningState(props));
195-
}
196-
if (props.onTransitionComplete) { props.onTransitionComplete(); }
197-
return this.setState(defaultState(props));
198-
case StateID.Active:
199-
case StateID.TransitToActiveStarted:
200-
return this.setState(transitToDefaultRunningState(props));
201-
case StateID.Default:
202-
case StateID.TransitToDefaultStarted:
203-
return this.setState(transitToActiveRunningState(props));
204-
case StateID.TransitToActiveRunning:
205-
if (props.onTransitionComplete) { props.onTransitionComplete(); }
206-
return this.setState(defaultState(props));
207-
case StateID.TransitToDefaultRunning:
208-
if (props.onTransitionComplete) { props.onTransitionComplete(); }
209-
return this.setState(activeState(props));
210-
default:
211-
throw new Error("invalid state transition");
212-
}
213-
case Action.TransitionStart:
214-
switch (state.id) {
215-
case StateID.TransitToActiveRunning:
216-
return this.setState(transitToActiveStartedState(props));
217-
case StateID.TransitToDefaultRunning:
218-
return this.setState(transitToDefaultStartedState(props));
219-
default:
220-
// We don't error out, because the workaround for transitionstart
221-
// could happen after transitionend.
222-
return;
223-
}
224-
case Action.TransitionComplete:
225-
switch (state.id) {
226-
case StateID.TransitToActiveRunning:
227-
case StateID.TransitToActiveStarted:
228-
if (props.onTransitionComplete) { props.onTransitionComplete(); }
229-
return this.setState(activeState(props));
230-
case StateID.TransitToDefaultRunning:
231-
case StateID.TransitToDefaultStarted:
232-
if (props.onTransitionComplete) { props.onTransitionComplete(); }
233-
return this.setState(defaultState(props));
234-
default:
235-
throw new Error("invalid state transition");
236-
}
237-
default:
100+
private handleTransitionComplete = () => this.dispatch(ActionID.TransitionComplete);
101+
private handleTransitionBegin = () => this.dispatch(ActionID.TransitionStart);
102+
103+
private dispatch(action: ActionID, props = this.props): void {
104+
const result = reduce(this.state, action, props);
105+
if (!result) { return; }
106+
const {state, pending} = result;
107+
let callback: any = undefined;
108+
if (this.cancelPending) { this.cancelPending(); this.cancelPending = null; }
109+
if (pending) { callback = () => { this.cancelPending = runInFrame(1, () => this.dispatch(pending)); }; }
110+
if (this.state === undefined) {
111+
this.state = state;
112+
return;
238113
}
239-
throw new Error("unexpected error");
114+
this.setState(state, callback);
240115
}
241116
}

0 commit comments

Comments
 (0)