Skip to content

Commit 9176c5a

Browse files
authored
fix: springs sometimes return undefined (pmndrs#1509)
* refactor: use a ref to capture the Controllers * fix: set the Springs when they're created useLayoutEffect isnt called when state is set in the hook
1 parent 8c395e5 commit 9176c5a

File tree

3 files changed

+14
-17
lines changed

3 files changed

+14
-17
lines changed

packages/core/src/Controller.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ export async function flushUpdate(
442442
* until they're given to `setSprings`.
443443
*/
444444
export function getSprings<State extends Lookup>(
445-
ctrl: Controller<State>,
445+
ctrl: Controller<Lookup<any>>,
446446
props?: OneOrMore<ControllerUpdate<State>>
447447
) {
448448
const springs = { ...ctrl.springs }
@@ -460,6 +460,7 @@ export function getSprings<State extends Lookup>(
460460
})
461461
})
462462
}
463+
setSprings(ctrl, springs)
463464
return springs
464465
}
465466

@@ -468,7 +469,7 @@ export function getSprings<State extends Lookup>(
468469
* whose key is not already in use.
469470
*/
470471
export function setSprings(
471-
ctrl: Controller,
472+
ctrl: Controller<Lookup<any>>,
472473
springs: SpringValues<UnknownProps>
473474
) {
474475
eachProp(springs, (spring, key) => {

packages/core/src/hooks/useSprings.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,17 @@ export function useSprings(
122122
[]
123123
)
124124

125-
const ctrls = [...state.ctrls]
125+
const ctrls = useRef([...state.ctrls])
126126
const updates: any[] = []
127127

128128
// Cache old controllers to dispose in the commit phase.
129129
const prevLength = usePrev(length) || 0
130-
const oldCtrls = ctrls.slice(length, prevLength)
130+
const oldCtrls = ctrls.current.slice(length, prevLength)
131131

132132
// Create new controllers when "length" increases, and destroy
133133
// the affected controllers when "length" decreases.
134134
useMemo(() => {
135-
ctrls.length = length
135+
ctrls.current.length = length
136136
declareUpdates(prevLength, length)
137137
}, [length])
138138

@@ -144,7 +144,9 @@ export function useSprings(
144144
/** Fill the `updates` array with declarative updates for the given index range. */
145145
function declareUpdates(startIndex: number, endIndex: number) {
146146
for (let i = startIndex; i < endIndex; i++) {
147-
const ctrl = ctrls[i] || (ctrls[i] = new Controller(null, state.flush))
147+
const ctrl =
148+
ctrls.current[i] ||
149+
(ctrls.current[i] = new Controller(null, state.flush))
148150

149151
const update: UseSpringProps<any> = propsFn
150152
? propsFn(i, ctrl)
@@ -159,7 +161,7 @@ export function useSprings(
159161
// New springs are created during render so users can pass them to
160162
// their animated components, but new springs aren't cached until the
161163
// commit phase (see the `useLayoutEffect` callback below).
162-
const springs = ctrls.map((ctrl, i) => getSprings(ctrl, updates[i]))
164+
const springs = ctrls.current.map((ctrl, i) => getSprings(ctrl, updates[i]))
163165

164166
const context = useContext(SpringContext)
165167
const prevContext = usePrev(context)
@@ -169,7 +171,7 @@ export function useSprings(
169171
layoutId.current++
170172

171173
// Replace the cached controllers.
172-
state.ctrls = ctrls
174+
state.ctrls = ctrls.current
173175

174176
// Flush the commit queue.
175177
const { queue } = state
@@ -185,10 +187,7 @@ export function useSprings(
185187
})
186188

187189
// Update existing controllers.
188-
each(ctrls, (ctrl, i) => {
189-
const values = springs[i]
190-
setSprings(ctrl, values)
191-
190+
each(ctrls.current, (ctrl, i) => {
192191
// Attach the controller to the local ref.
193192
ref?.add(ctrl)
194193

packages/core/src/hooks/useTransition.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
inferTo,
3131
replaceRef,
3232
} from '../helpers'
33-
import { Controller, getSprings, setSprings } from '../Controller'
33+
import { Controller, getSprings } from '../Controller'
3434
import { SpringContext } from '../SpringContext'
3535
import { SpringRef } from '../SpringRef'
3636
import { TransitionPhase } from '../TransitionPhase'
@@ -321,13 +321,10 @@ export function useTransition(
321321

322322
useLayoutEffect(
323323
() => {
324-
each(changes, ({ phase, springs, payload }, t) => {
324+
each(changes, ({ phase, payload }, t) => {
325325
const { ctrl } = t
326326
t.phase = phase
327327

328-
// Save any springs created this render.
329-
setSprings(ctrl, springs)
330-
331328
// Attach the controller to our local ref.
332329
ref?.add(ctrl)
333330

0 commit comments

Comments
 (0)