1
- import { useMemo , RefObject , useImperativeHandle } from 'react'
1
+ import { useMemo , RefObject , useImperativeHandle , useState } from 'react'
2
2
import { useLayoutEffect } from 'react-layout-effect'
3
3
import { is , each , usePrev , useOnce , UnknownProps , Merge } from 'shared'
4
4
@@ -70,23 +70,36 @@ export function useSprings(
70
70
deps ?: any [ ]
71
71
) : any {
72
72
const propsFn = is . fun ( props ) && props
73
- if ( deps ) deps = deps . concat ( length )
73
+ if ( propsFn && ! deps ) deps = [ ]
74
74
75
75
// The "ref" prop is taken from the props of the first spring only.
76
76
// The ref is assumed to *never* change after the first render.
77
77
let ref : RefObject < SpringHandle > | undefined
78
78
79
- const ctrls : Controller [ ] = useMemoOne ( ( ) => [ ] , [ ] )
79
+ const [ ctrls ] = useState < Controller [ ] > ( [ ] )
80
80
const updates : ControllerProps [ ] = [ ]
81
81
const prevLength = usePrev ( length ) || 0
82
+
83
+ // Create new controllers when "length" increases, and destroy
84
+ // the affected controllers when "length" decreases.
82
85
useMemoOne ( ( ) => {
86
+ // Note: Length changes are unsafe in React concurrent mode.
83
87
if ( prevLength > length ) {
84
88
for ( let i = length ; i < prevLength ; i ++ ) {
85
89
ctrls [ i ] . dispose ( )
86
90
}
87
91
}
88
92
ctrls . length = length
89
- for ( let i = 0 ; i < length ; i ++ ) {
93
+ getUpdates ( prevLength , length )
94
+ } , [ length ] )
95
+
96
+ // Update existing controllers when "deps" are changed.
97
+ useMemoOne ( ( ) => {
98
+ getUpdates ( 0 , prevLength )
99
+ } , deps )
100
+
101
+ function getUpdates ( startIndex : number , endIndex : number ) {
102
+ for ( let i = startIndex ; i < endIndex ; i ++ ) {
90
103
const ctrl = ctrls [ i ] || ( ctrls [ i ] = new Controller ( ) )
91
104
const update : UseSpringProps < any > = propsFn
92
105
? propsFn ( i , ctrl )
@@ -106,7 +119,19 @@ export function useSprings(
106
119
}
107
120
}
108
121
}
109
- } , deps )
122
+ }
123
+
124
+ // Controllers are not updated until the commit phase.
125
+ useLayoutEffect ( ( ) => {
126
+ each ( updates , ( update , i ) => ctrls [ i ] . update ( update ) )
127
+ if ( ! ref ) {
128
+ each ( ctrls , ctrl => ctrl . start ( ) )
129
+ }
130
+ } )
131
+
132
+ useOnce ( ( ) => ( ) => {
133
+ each ( ctrls , ctrl => ctrl . dispose ( ) )
134
+ } )
110
135
111
136
const api = useMemo (
112
137
( ) : SpringHandle => ( {
@@ -136,20 +161,8 @@ export function useSprings(
136
161
137
162
useImperativeHandle ( ref , ( ) => api )
138
163
139
- const isRenderDriven = ! propsFn && arguments . length < 3
140
- if ( isRenderDriven ) deps = undefined
141
-
142
- useLayoutEffect ( ( ) => {
143
- each ( updates , ( update , i ) => ctrls [ i ] . update ( update ) )
144
- if ( ! ref ) {
145
- each ( ctrls , ctrl => ctrl . start ( ) )
146
- }
147
- } , deps )
148
-
149
- useOnce ( ( ) => ( ) => {
150
- each ( ctrls , ctrl => ctrl . dispose ( ) )
151
- } )
152
-
153
164
const values = ctrls . map ( ctrl => ( { ...ctrl . springs } ) )
154
- return isRenderDriven ? values : [ values , api . update , api . stop ]
165
+ return propsFn || arguments . length == 3
166
+ ? [ values , api . update , api . stop ]
167
+ : values
155
168
}
0 commit comments