Skip to content

Commit 509d58c

Browse files
committed
fix: type errors in useTransition
and add an "update" method to the ref api
1 parent 17eea8f commit 509d58c

File tree

1 file changed

+52
-30
lines changed

1 file changed

+52
-30
lines changed

packages/core/src/useTransition.tsx

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
import React, { useEffect, useRef, useImperativeHandle, ReactNode } from 'react'
2-
import { is, toArray, useForceUpdate, useOnce, each } from 'shared'
1+
import React, {
2+
useEffect,
3+
useRef,
4+
useImperativeHandle,
5+
ReactNode,
6+
useMemo,
7+
} from 'react'
8+
import { is, toArray, useForceUpdate, useOnce, each, OneOrMore } from 'shared'
9+
import { now } from 'shared/globals'
10+
311
import { callProp, interpolateTo } from './helpers'
12+
import { SpringHandle } from './types/spring'
413
import { Controller } from './Controller'
5-
import { now } from 'shared/globals'
614

715
// TODO: convert to "const enum" once Babel supports it
816
type Phase = number
@@ -24,19 +32,22 @@ export type ItemKeys<T> =
2432
| number
2533
| null
2634

27-
export function useTransition<T>(
28-
data: T | readonly T[],
29-
props: any,
30-
deps?: any
31-
) {
35+
export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
3236
const { key, ref, reset, sort, trail = 0, expires = Infinity } = props
3337

3438
// Every item has its own transition.
35-
const items = toArray(data)
39+
const items = toArray<unknown>(data)
3640
const transitions: Transition[] = []
3741

3842
// Keys help with reusing transitions between renders.
39-
const keys = is.und(key) ? items : is.fun(key) ? items.map(key) : toArray(key)
43+
// The `key` prop can be undefined (which means the items themselves are used
44+
// as keys), or a function (which maps each item to its key), or an array of
45+
// keys (which are assigned to each item by index).
46+
const keys: any[] = is.und(key)
47+
? items
48+
: is.fun(key)
49+
? items.map(key)
50+
: toArray(key)
4051

4152
// The "onRest" callbacks need a ref to the latest transitions.
4253
const usedTransitions = useRef<Transition[] | null>(null)
@@ -49,7 +60,7 @@ export function useTransition<T>(
4960
useOnce(() => () =>
5061
each(usedTransitions.current!, t => {
5162
if (t.expiresBy) clearTimeout(t.expirationId)
52-
t.spring.destroy()
63+
t.ctrl.dispose()
5364
})
5465
)
5566

@@ -73,7 +84,7 @@ export function useTransition<T>(
7384
key: keys[i],
7485
item,
7586
phase: MOUNT,
76-
spring: new Controller(),
87+
ctrl: new Controller(),
7788
})
7889
})
7990

@@ -132,6 +143,7 @@ export function useTransition<T>(
132143
} else return
133144
}
134145

146+
// The payload is used to update the spring props once the current render is committed.
135147
const payload: any = {
136148
// When "to" is a function, it can return (1) an array of "useSpring" props,
137149
// (2) an async function, or (3) an object with any "useSpring" props.
@@ -154,7 +166,7 @@ export function useTransition<T>(
154166
} else {
155167
// Postpone dismounts while other controllers are active.
156168
const transitions = usedTransitions.current!
157-
if (transitions.every(t => t.spring.idle)) {
169+
if (transitions.every(t => t.ctrl.idle)) {
158170
forceUpdate()
159171
} else if (expires < Infinity) {
160172
t.expirationId = setTimeout(forceUpdate, expires)
@@ -171,46 +183,56 @@ export function useTransition<T>(
171183
if (t.phase > MOUNT) {
172184
change.payload = payload
173185
} else {
174-
t.spring.update(payload)
186+
t.ctrl.update(payload)
175187
}
176188
})
177189

178-
useImperativeHandle(
179-
ref,
180-
() => ({
190+
const api = useMemo(
191+
(): SpringHandle => ({
181192
get controllers() {
182-
return usedTransitions.current!.map(t => t.spring)
193+
return usedTransitions.current!.map(t => t.ctrl)
183194
},
184-
start: () =>
185-
Promise.all(
186-
usedTransitions.current!.map(
187-
t => new Promise(done => t.spring.start(done))
195+
update(props) {
196+
each(usedTransitions.current!, (t, i) =>
197+
t.ctrl.update(
198+
is.fun(props) ? props(i, t.ctrl) : is.arr(props) ? props[i] : props
188199
)
189-
),
190-
stop: (finished?: boolean) =>
191-
each(usedTransitions.current!, t => t.spring.stop(finished)),
200+
)
201+
return api
202+
},
203+
async start() {
204+
const transitions = usedTransitions.current!
205+
const results = await Promise.all(transitions.map(t => t.ctrl.start()))
206+
return {
207+
value: results.map(result => result.value),
208+
finished: results.every(result => result.finished),
209+
}
210+
},
211+
stop: keys => each(usedTransitions.current!, t => t.ctrl.stop(keys)),
192212
}),
193213
[]
194214
)
195215

216+
useImperativeHandle(ref, () => api)
217+
196218
useEffect(
197219
() => {
198220
each(changes, ({ phase, payload }, t) => {
199221
t.phase = phase
200-
if (payload) t.spring.update(payload)
201-
if (!ref) t.spring.start()
222+
if (payload) t.ctrl.update(payload)
223+
if (!ref) t.ctrl.start()
202224
})
203225
},
204226
reset ? void 0 : deps
205227
)
206228

207229
return (render: (props: any, item: T) => ReactNode) =>
208230
transitions.map(t => {
209-
const elem: any = render({ ...t.spring.animated }, t.item)
231+
const elem: any = render({ ...t.ctrl.springs }, t.item)
210232
return elem && elem.type ? (
211233
<elem.type
212234
{...elem.props}
213-
key={is.str(t.key) || is.num(t.key) ? t.key : t.spring.id}
235+
key={is.str(t.key) || is.num(t.key) ? t.key : t.ctrl.id}
214236
ref={elem.ref}
215237
/>
216238
) : (
@@ -227,8 +249,8 @@ interface Change {
227249
interface Transition<T = any> {
228250
key: any
229251
item: T
252+
ctrl: Controller
230253
phase: Phase
231-
spring: Controller
232254
/** Destroy no later than this date */
233255
expiresBy?: number
234256
expirationId?: number

0 commit comments

Comments
 (0)