Skip to content

Instantly share code, notes, and snippets.

@joshsalverda
Last active May 22, 2023 09:49
Show Gist options
  • Save joshsalverda/d808d92f46a7085be062b2cbde978ae6 to your computer and use it in GitHub Desktop.
Save joshsalverda/d808d92f46a7085be062b2cbde978ae6 to your computer and use it in GitHub Desktop.
Custom useFieldArray hook for formik using immutability-helper
import {useCallback, useRef, useEffect} from 'react'
import {useField, useFormikContext} from 'formik'
import update from 'immutability-helper'
const useFieldArray = props => {
const [field, meta] = useField(props)
const fieldArray = useRef(field.value)
const {setFieldValue} = useFormikContext()
useEffect(() => {
fieldArray.current = field.value
}, [field.value])
const push = useCallback(value => {
fieldArray.current = update(fieldArray.current, {
$push: [value]
})
setFieldValue(field.name, fieldArray.current)
}, [field.name, setFieldValue])
const swap = useCallback((indexA, indexB) => {
const swapA = fieldArray.current[indexA]
const swapB = fieldArray.current[indexB]
fieldArray.current = update(fieldArray.current, {
$splice: [[indexA, 1, swapB], [indexB, 1, swapA]]
})
setFieldValue(field.name, fieldArray.current)
}, [field.name, setFieldValue])
const move = useCallback((from, to) => {
const toMove = fieldArray.current[from]
fieldArray.current = update(fieldArray.current, {
$splice: [[from, 1], [to, 0, toMove]]
})
setFieldValue(field.name, fieldArray.current)
}, [field.name, setFieldValue])
const insert = useCallback((index, value) => {
fieldArray.current = update(fieldArray.current, {
$splice: [[index, 0, value]]
})
setFieldValue(field.name, fieldArray.current)
}, [field.name, setFieldValue])
const unshift = useCallback(value => {
fieldArray.current = update(fieldArray.current, {
$unshift: [value]
})
setFieldValue(field.name, fieldArray.current)
return fieldArray.current.length
}, [field.name, setFieldValue])
const remove = useCallback(index => {
const removedItem = fieldArray.current[index]
fieldArray.current = update(fieldArray.current, {
$splice: [[index, 1]]
})
setFieldValue(field.name, fieldArray.current)
return removedItem
}, [field.name, setFieldValue])
const pop = useCallback(() => {
const lastIndex = fieldArray.current.length - 1
const poppedItem = fieldArray.current[lastIndex]
fieldArray.current = update(fieldArray.current, {
$splice: [[lastIndex, 1]]
})
setFieldValue(field.name, fieldArray.current)
return poppedItem
}, [field.name, setFieldValue])
const replace = useCallback((index, value) => {
fieldArray.current = update(fieldArray.current, {
$splice: [[index, 1, value]]
})
setFieldValue(field.name, fieldArray.current)
}, [field.name, setFieldValue])
return [
field,
meta,
{
push,
swap,
move,
insert,
unshift,
remove,
pop,
replace
}
]
}
export {useFieldArray}
@Bulletninja
Copy link

Thanks a lot!

@joshsalverda
Copy link
Author

joshsalverda commented Sep 16, 2020

I was having weird sync issues again recently (not sure what changed 🤔)

Haven't looked into it too closely, but as a quick fix changing from useEffect to useLayoutEffect seems to work. It can delay the rendering if you are pushing in lots of items, but at least no data gets lost...

@abranhe
Copy link

abranhe commented Jun 3, 2021

We need this as a part of formik.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment