Skip to content

Commit d77bbbf

Browse files
committed
Switched to @mpenney99's solution
1 parent af1c2cf commit d77bbbf

File tree

3 files changed

+58
-87
lines changed

3 files changed

+58
-87
lines changed

src/useConstantCallback.js

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,15 @@
22
import * as React from 'react'
33

44
/**
5-
* Creates a callback, with dependencies, that will be
6-
* instance === for the lifetime of the component.
5+
* Creates a callback, even with closures, that will be
6+
* instance === for the lifetime of the component, always
7+
* calling the most recent version of the function and its
8+
* closures.
79
*/
8-
export default function useConstantCallback(callback, deps) {
9-
// initialize refs on first render
10-
const refs = deps.map(React.useRef)
11-
// update refs on each additional render
12-
deps.forEach((dep, index) => (refs[index].current = dep))
13-
// eslint-disable-next-line react-hooks/exhaustive-deps
14-
const constant = React.useRef((...args) => {
15-
// This if seems weird, but if args is [], then the
16-
// first param will be the refs.map()
17-
if (args && args.length) {
18-
callback(
19-
...args,
20-
refs.map(ref => ref.current)
21-
)
22-
} else {
23-
callback(
24-
undefined,
25-
refs.map(ref => ref.current)
26-
)
27-
}
28-
}, [])
29-
return constant.current
10+
export default function useConstantCallback(callback) {
11+
const ref = React.useRef(callback)
12+
React.useEffect(() => {
13+
ref.current = callback
14+
})
15+
return React.useCallback((...args) => ref.current.apply(null, args), [])
3016
}

src/useConstantCallback.test.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@ describe('useConstantCallback', () => {
1414
const [name, setName] = React.useState('John')
1515
const [age, setAge] = React.useState(20)
1616
const [isAdmin, setAdmin] = React.useState(false)
17-
const constantCallback = useConstantCallback(
18-
(time, [name, age, isAdmin]) => {
19-
expect(typeof time).toBe('number')
20-
callback(name, age, isAdmin)
21-
},
22-
[name, age, isAdmin]
23-
)
17+
const constantCallback = useConstantCallback(time => {
18+
expect(typeof time).toBe('number')
19+
callback(name, age, isAdmin)
20+
})
2421
const callbackRef = React.useRef(constantCallback)
2522
expect(callbackRef.current).toBe(constantCallback)
2623
return (

src/useField.js

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -168,64 +168,52 @@ function useField<FormValues: FormValuesShape>(
168168
}
169169
return undefined
170170
},
171-
onBlur: useConstantCallback(
172-
(event: ?SyntheticFocusEvent<*>, [form, format, formatOnBlur, state]) => {
173-
state.blur()
174-
if (formatOnBlur) {
175-
/**
176-
* Here we must fetch the value directly from Final Form because we cannot
177-
* trust that our `state` closure has the most recent value. This is a problem
178-
* if-and-only-if the library consumer has called `onChange()` immediately
179-
* before calling `onBlur()`, but before the field has had a chance to receive
180-
* the value update from Final Form.
181-
*/
182-
const fieldState: any = form.getFieldState(state.name)
183-
state.change(format(fieldState.value, state.name))
184-
}
185-
},
186-
[form, format, formatOnBlur, state]
187-
),
188-
onChange: useConstantCallback(
189-
(
190-
event: SyntheticInputEvent<*> | any,
191-
[component, name, parse, state, type, _value]
192-
) => {
193-
// istanbul ignore next
194-
if (process.env.NODE_ENV !== 'production' && event && event.target) {
195-
const targetType = event.target.type
196-
const unknown =
197-
~['checkbox', 'radio', 'select-multiple'].indexOf(targetType) &&
198-
!type &&
199-
component !== 'select'
200-
201-
const value: any =
202-
targetType === 'select-multiple' ? state.value : _value
203-
204-
if (unknown) {
205-
console.error(
206-
`You must pass \`type="${
207-
targetType === 'select-multiple' ? 'select' : targetType
208-
}"\` prop to your Field(${name}) component.\n` +
209-
`Without it we don't know how to unpack your \`value\` prop - ${
210-
Array.isArray(value) ? `[${value}]` : `"${value}"`
211-
}.`
212-
)
213-
}
214-
}
171+
onBlur: useConstantCallback((event: ?SyntheticFocusEvent<*>) => {
172+
state.blur()
173+
if (formatOnBlur) {
174+
/**
175+
* Here we must fetch the value directly from Final Form because we cannot
176+
* trust that our `state` closure has the most recent value. This is a problem
177+
* if-and-only-if the library consumer has called `onChange()` immediately
178+
* before calling `onBlur()`, but before the field has had a chance to receive
179+
* the value update from Final Form.
180+
*/
181+
const fieldState: any = form.getFieldState(state.name)
182+
state.change(format(fieldState.value, state.name))
183+
}
184+
}),
185+
onChange: useConstantCallback((event: SyntheticInputEvent<*> | any) => {
186+
// istanbul ignore next
187+
if (process.env.NODE_ENV !== 'production' && event && event.target) {
188+
const targetType = event.target.type
189+
const unknown =
190+
~['checkbox', 'radio', 'select-multiple'].indexOf(targetType) &&
191+
!type &&
192+
component !== 'select'
215193

216194
const value: any =
217-
event && event.target
218-
? getValue(event, state.value, _value, isReactNative)
219-
: event
220-
state.change(parse(value, name))
221-
},
222-
[component, name, parse, state, type, _value]
223-
),
224-
onFocus: useConstantCallback(
225-
(event: ?SyntheticFocusEvent<*>, [state]) => {
226-
state.focus()
227-
},
228-
[state]
195+
targetType === 'select-multiple' ? state.value : _value
196+
197+
if (unknown) {
198+
console.error(
199+
`You must pass \`type="${
200+
targetType === 'select-multiple' ? 'select' : targetType
201+
}"\` prop to your Field(${name}) component.\n` +
202+
`Without it we don't know how to unpack your \`value\` prop - ${
203+
Array.isArray(value) ? `[${value}]` : `"${value}"`
204+
}.`
205+
)
206+
}
207+
}
208+
209+
const value: any =
210+
event && event.target
211+
? getValue(event, state.value, _value, isReactNative)
212+
: event
213+
state.change(parse(value, name))
214+
}),
215+
onFocus: useConstantCallback((event: ?SyntheticFocusEvent<*>) =>
216+
state.focus()
229217
)
230218
}
231219

0 commit comments

Comments
 (0)