@@ -120,6 +120,21 @@ describe('packages/hooks/useControlled', () => {
120120 } ) ;
121121 expect ( result . current . value ) . toBe ( 'apple' ) ;
122122 } ) ;
123+
124+ test ( 'updateValue accepts a function for functional updates' , ( ) => {
125+ const handler = jest . fn ( ) ;
126+ const { result } = renderUseControlledHook < number > ( 5 , handler ) ;
127+ result . current . updateValue ( prev => prev + 1 ) ;
128+ expect ( handler ) . toHaveBeenCalledWith ( 6 ) ;
129+ } ) ;
130+
131+ test ( 'functional update uses current controlled value' , ( ) => {
132+ const handler = jest . fn ( ) ;
133+ const { result, rerender } = renderUseControlledHook < number > ( 10 , handler ) ;
134+ rerender ( 20 ) ;
135+ result . current . updateValue ( prev => prev * 2 ) ;
136+ expect ( handler ) . toHaveBeenCalledWith ( 40 ) ;
137+ } ) ;
123138 } ) ;
124139
125140 describe ( 'Uncontrolled' , ( ) => {
@@ -170,6 +185,49 @@ describe('packages/hooks/useControlled', () => {
170185 expect ( result . current . value ) . toBe ( 'apple' ) ;
171186 expect ( handler ) . not . toHaveBeenCalled ( ) ;
172187 } ) ;
188+
189+ test ( 'updateValue accepts a function for functional updates' , ( ) => {
190+ const handler = jest . fn ( ) ;
191+ const { result } = renderUseControlledHook < number > ( undefined , handler , 5 ) ;
192+ act ( ( ) => {
193+ result . current . updateValue ( prev => prev + 1 ) ;
194+ } ) ;
195+ expect ( handler ) . toHaveBeenCalledWith ( 6 ) ;
196+ expect ( result . current . value ) . toBe ( 6 ) ;
197+ } ) ;
198+
199+ test ( 'functional update uses current uncontrolled value' , ( ) => {
200+ const handler = jest . fn ( ) ;
201+ const { result } = renderUseControlledHook < number > (
202+ undefined ,
203+ handler ,
204+ 10 ,
205+ ) ;
206+ act ( ( ) => {
207+ result . current . updateValue ( prev => prev * 2 ) ;
208+ } ) ;
209+ expect ( handler ) . toHaveBeenCalledWith ( 20 ) ;
210+ expect ( result . current . value ) . toBe ( 20 ) ;
211+ } ) ;
212+
213+ test ( 'functional update avoids stale closures' , ( ) => {
214+ const handler = jest . fn ( ) ;
215+ const { result } = renderUseControlledHook < number > ( undefined , handler , 0 ) ;
216+
217+ // Simulate multiple updates that might capture stale values
218+ act ( ( ) => {
219+ result . current . updateValue ( prev => prev + 1 ) ;
220+ result . current . updateValue ( prev => prev + 1 ) ;
221+ result . current . updateValue ( prev => prev + 1 ) ;
222+ } ) ;
223+
224+ // With functional updates, each update should use the latest value
225+ expect ( handler ) . toHaveBeenCalledTimes ( 3 ) ;
226+ expect ( handler ) . toHaveBeenNthCalledWith ( 1 , 1 ) ;
227+ expect ( handler ) . toHaveBeenNthCalledWith ( 2 , 2 ) ;
228+ expect ( handler ) . toHaveBeenNthCalledWith ( 3 , 3 ) ;
229+ expect ( result . current . value ) . toBe ( 3 ) ;
230+ } ) ;
173231 } ) ;
174232
175233 describe ( 'Within test component' , ( ) => {
0 commit comments