Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the merge and removeBatch mutators #27

Merged
merged 2 commits into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ const customer = form.mutators.pop('customers')

- [Mutators](#mutators)
- [`form.mutators.insert(name: string, index: number, value: any) => undefined`](#formmutatorsinsertname-string-index-number-value-any--undefined)
- [`form.mutators.merge(name: string, value: Array<any>) => void`](#formmutatorsmergename-string-value-arrayany--void)
- [`form.mutators.move(name: string, from: number, to: number) => undefined`](#formmutatorsmovename-string-from-number-to-number--undefined)
- [`form.mutators.pop(name: string) => any`](#formmutatorspopname-string--any)
- [`form.mutators.push(name: string, value: any) => void`](#formmutatorspushname-string-value-any--void)
- [`form.mutators.remove(name: string, index: number) => any`](#formmutatorsremovename-string-index-number--any)
- [`form.mutators.removeBatch(name: string, indexes: Array<number>) => undefined`](#formmutatorsremovebatchname-string-indexes-arraynumber--undefined)
- [`form.mutators.shift(name: string) => any`](#formmutatorsshiftname-string--any)
- [`form.mutators.swap(name: string, indexA: number, indexB: number) => void`](#formmutatorsswapname-string-indexa-number-indexb-number--void)
- [`form.mutators.update(name: string, index: number, value: any) => void`](#formmutatorsupdatename-string-index-number-value-any--void)
Expand All @@ -67,6 +69,10 @@ const customer = form.mutators.pop('customers')

Inserts a value into the specified index of the array field.

### `form.mutators.merge(name: string, value: Array<any>) => void`

Merges an array at the end of the array field.

### `form.mutators.move(name: string, from: number, to: number) => undefined`

Moves a value from one index to another index in the array field.
Expand All @@ -84,6 +90,10 @@ Pushes a value onto the end of an array field.
Removes a value from the specified index of the array field. Returns the removed
value.

### `form.mutators.removeBatch(name: string, indexes: Array<number>) => undefined`

Removes the values at the specified indexes of the array field.

### `form.mutators.shift(name: string) => any`

Removes a value from the beginning of the array field. Returns the value.
Expand Down
2 changes: 2 additions & 0 deletions src/index.d.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ const form = createForm({
const mutators: Mutators = (form.mutators as any) as Mutators

mutators.insert('customers', 0, { firstName: '', lastName: '' })
mutators.merge('customers', [{ firstName: '', lastName: '' }, { firstName: '', lastName: '' }])
mutators.move('customers', 0, 1)
const customer = mutators.pop('customers')
mutators.push('customers', { firstName: '', lastName: '' })
mutators.removeBatch('customers', [0])
const removed = mutators.remove('customers', 0)
const shifted = mutators.shift('customers')
mutators.swap('customers', 0, 1)
Expand Down
6 changes: 6 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Mutator } from 'final-form'

export const insert: Mutator
export const merge: Mutator
export const move: Mutator
export const pop: Mutator
export const push: Mutator
export const removeBatch: Mutator
export const remove: Mutator
export const shift: Mutator
export const swap: Mutator
Expand All @@ -12,9 +14,11 @@ export const unshift: Mutator

export interface DefaultType {
insert: Mutator
merge: Mutator
move: Mutator
pop: Mutator
push: Mutator
removeBatch: Mutator
remove: Mutator
shift: Mutator
swap: Mutator
Expand All @@ -28,10 +32,12 @@ export default d
/** The shape of the mutators once final-form has bound them to state */
export interface Mutators {
insert: (name: string, index: number, value: any) => void
merge: (name: string, value: Array<any>) => void,
move: (name: string, from: number, to: number) => void
pop: (name: string) => any
push: (name: string, value: any) => void
remove: (name: string, index: number) => any
removeBatch: (name: string, indexes: Array<number>) => any
shift: (name: string) => any
swap: (name: string, indexA: number, indexB: number) => void
update: (name: string, index: number, value: any) => void
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
// @flow
import type { Mutator } from 'final-form'
import insert from './insert'
import merge from './merge'
import move from './move'
import pop from './pop'
import push from './push'
import remove from './remove'
import removeBatch from './removeBatch'
import shift from './shift'
import swap from './swap'
import unshift from './unshift'
import update from './update'

const mutators: { [string]: Mutator } = {
insert,
merge,
move,
pop,
push,
remove,
removeBatch,
shift,
swap,
unshift,
Expand Down
2 changes: 2 additions & 0 deletions src/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ declare export default DefaultType
/** The shape of the mutators once final-form has bound them to state */
export type Mutators = {
insert: (name: string, index: number, value: any) => void,
merge: (name: string, value: Array<any>) => void,
move: (name: string, from: number, to: number) => void,
pop: (name: string) => any,
push: (name: string, value: any) => void,
remove: (name: string, index: number) => any,
removeBatch: (name: string, indexes: Array<number>) => any,
shift: (name: string) => any,
swap: (name: string, indexA: number, indexB: number) => void,
update: (name: string, index: number, value: any) => void,
Expand Down
16 changes: 16 additions & 0 deletions src/merge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @flow
import type { MutableState, Mutator, Tools } from 'final-form'

const merge: Mutator = (
[name, value]: any[],
state: MutableState,
{ changeValue }: Tools
) => {
changeValue(
state,
name,
(array: ?(any[])): any[] => (array ? [...array, ...value] : value)
)
}

export default merge
35 changes: 35 additions & 0 deletions src/merge.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import merge from './merge'

describe('merge', () => {
const getOp = value => {
const changeValue = jest.fn()
merge(['foo', value], {}, { changeValue })
return changeValue.mock.calls[0][2]
}

it('should call changeValue once', () => {
const changeValue = jest.fn()
const state = {}
const result = merge(['foo', ['bar', 'baz']], state, { changeValue })
expect(result).toBeUndefined()
expect(changeValue).toHaveBeenCalled()
expect(changeValue).toHaveBeenCalledTimes(1)
expect(changeValue.mock.calls[0][0]).toBe(state)
expect(changeValue.mock.calls[0][1]).toBe('foo')
expect(typeof changeValue.mock.calls[0][2]).toBe('function')
})

it('should turn undefined into an array with two values', () => {
const op = getOp(['bar', 'baz'])
const result = op(undefined)
expect(Array.isArray(result)).toBe(true)
expect(result).toEqual(['bar', 'baz'])
})

it('should merge the array at the end of the original array', () => {
const op = getOp(['d', 'e'])
const result = op(['a', 'b', 'c'])
expect(Array.isArray(result)).toBe(true)
expect(result).toEqual(['a', 'b', 'c', 'd', 'e'])
})
})
36 changes: 36 additions & 0 deletions src/removeBatch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @flow
import type { MutableState, Mutator, Tools } from 'final-form'

const removeBatch: Mutator = (
[name, indexes]: any[],
state: MutableState,
{ changeValue }: Tools
) => {
changeValue(
state,
name,
(array: ?(any[])): ?(any[]) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could simply be:
array => array && indexes ? array.filter((item, i) => !indexes.includes(i)) : array

Although much simpler, the performance is significantly worse. Not sure what should be the goal here, since probably there won't be hundreds/thousands of item in the field array.

Any thoughts?

if (!array || !indexes) {
return array
}

let mask = new Array(indexes.length)
for (let i = 0; i < indexes.length; i++) {
mask[indexes[i]] = true
}

let offset = 0
for (let i = 0; i < array.length; i++) {
if (mask[i] === undefined) {
array[offset] = array[i]
offset++
}
}

array.length = offset
return array
}
)
}

export default removeBatch
41 changes: 41 additions & 0 deletions src/removeBatch.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import removeBatch from './removeBatch'

describe('merge', () => {
const getOp = value => {
const changeValue = jest.fn()
removeBatch(['foo', value], {}, { changeValue })
return changeValue.mock.calls[0][2]
}

it('should call changeValue once', () => {
const changeValue = jest.fn()
const state = {}
const result = removeBatch(['foo', [1, 2]], state, { changeValue })
expect(result).toBeUndefined()
expect(changeValue).toHaveBeenCalled()
expect(changeValue).toHaveBeenCalledTimes(1)
expect(changeValue.mock.calls[0][0]).toBe(state)
expect(changeValue.mock.calls[0][1]).toBe('foo')
expect(typeof changeValue.mock.calls[0][2]).toBe('function')
})

it('should return undefined if array is undefined', () => {
const op = getOp([0, 1])
const result = op(undefined)
expect(result).toBeUndefined()
})

it('should return the original array if no indexes are specified to be removed', () => {
const op = getOp([])
const result = op(['a', 'b', 'c', 'd', 'e'])
expect(Array.isArray(result)).toBe(true)
expect(result).toEqual(['a', 'b', 'c', 'd', 'e'])
})

it('should remove the values at the specified indexes', () => {
const op = getOp([1, 3])
const result = op(['a', 'b', 'c', 'd', 'e'])
expect(Array.isArray(result)).toBe(true)
expect(result).toEqual(['a', 'c', 'e'])
})
})