Skip to content

Commit d7c4856

Browse files
committed
Change pop mutator to call remove mutator.
Change `remove` and `removeBatch` behavior to set array value to undefined when all items have been removed.
1 parent c7bf418 commit d7c4856

File tree

6 files changed

+53
-78
lines changed

6 files changed

+53
-78
lines changed

src/pop.js

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,17 @@
11
// @flow
22
import type { MutableState, Mutator, Tools } from 'final-form'
3-
import { escapeRegexTokens } from './utils'
3+
import remove from './remove'
44

55
const pop: Mutator<any> = (
66
[name]: any[],
77
state: MutableState<any>,
8-
{ changeValue }: Tools<any>
8+
tools: Tools<any>
99
) => {
10-
let result
11-
let removedIndex: ?number
12-
changeValue(state, name, (array: ?(any[])): ?(any[]) => {
13-
if (array) {
14-
if (!array.length) {
15-
return []
16-
}
17-
removedIndex = array.length - 1
18-
result = array[removedIndex]
19-
return array.slice(0, removedIndex)
20-
}
21-
})
22-
23-
// now we have to remove any subfields for our index,
24-
if (removedIndex !== undefined) {
25-
const pattern = new RegExp(
26-
`^${escapeRegexTokens(name)}\\[${removedIndex}].*`
27-
)
28-
Object.keys(state.fields).forEach(key => {
29-
if (pattern.test(key)) {
30-
delete state.fields[key]
31-
}
32-
})
33-
}
34-
return result
10+
const { getIn } = tools;
11+
const array = getIn(state.formState.values, name)
12+
return array && array.length > 0
13+
? remove([name, array.length - 1], state, tools)
14+
: undefined
3515
}
3616

3717
export default pop

src/pop.test.js

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('pop', () => {
2323
}
2424
}
2525
}
26-
const result = pop(['foo'], state, { changeValue })
26+
const result = pop(['foo'], state, { changeValue, getIn, setIn })
2727
expect(result).toBeUndefined()
2828
expect(changeValue).toHaveBeenCalled()
2929
expect(changeValue).toHaveBeenCalledTimes(1)
@@ -33,71 +33,59 @@ describe('pop', () => {
3333
})
3434

3535
it('should return undefined if array is undefined', () => {
36-
const changeValue = jest.fn()
36+
// implementation of changeValue taken directly from Final Form
37+
const changeValue = (state, name, mutate) => {
38+
const before = getIn(state.formState.values, name)
39+
const after = mutate(before)
40+
state.formState.values = setIn(state.formState.values, name, after) || {}
41+
}
3742
const state = {
3843
formState: {
3944
values: {
40-
foo: ['one', 'two']
45+
foo: undefined
4146
}
4247
},
43-
fields: {
44-
'foo[0]': {
45-
name: 'foo[0]',
46-
touched: true,
47-
error: 'First Error'
48-
},
49-
'foo[1]': {
50-
name: 'foo[1]',
51-
touched: false,
52-
error: 'Second Error'
53-
}
54-
}
48+
fields: {}
5549
}
56-
const returnValue = pop(['foo'], state, { changeValue })
57-
const op = changeValue.mock.calls[0][2]
50+
const returnValue = pop(['foo'], state, { changeValue, getIn, setIn })
5851
expect(returnValue).toBeUndefined()
59-
const result = op(undefined)
52+
const result = state.formState.foo
6053
expect(result).toBeUndefined()
6154
})
6255

6356
it('should return empty array if array is empty', () => {
64-
const changeValue = jest.fn()
57+
// implementation of changeValue taken directly from Final Form
58+
const changeValue = (state, name, mutate) => {
59+
const before = getIn(state.formState.values, name)
60+
const after = mutate(before)
61+
state.formState.values = setIn(state.formState.values, name, after) || {}
62+
}
6563
const state = {
6664
formState: {
6765
values: {
68-
foo: ['one', 'two']
66+
foo: []
6967
}
7068
},
71-
fields: {
72-
'foo[0]': {
73-
name: 'foo[0]',
74-
touched: true,
75-
error: 'First Error'
76-
},
77-
'foo[1]': {
78-
name: 'foo[1]',
79-
touched: false,
80-
error: 'Second Error'
81-
}
82-
}
69+
fields: {}
8370
}
84-
const returnValue = pop(['foo'], state, { changeValue })
85-
const op = changeValue.mock.calls[0][2]
71+
const returnValue = pop(['foo'], state, { changeValue, getIn, setIn })
8672
expect(returnValue).toBeUndefined()
87-
const result = op([])
73+
const result = state.formState.values.foo
8874
expect(Array.isArray(result)).toBe(true)
8975
expect(result.length).toBe(0)
9076
})
9177

9278
it('should pop value off the end of array and return it', () => {
93-
let result
94-
const changeValue = jest.fn((args, state, op) => {
95-
result = op(['a', 'b', 'c'])
96-
})
79+
// implementation of changeValue taken directly from Final Form
80+
const changeValue = jest.fn((state, name, mutate) => {
81+
const before = getIn(state.formState.values, name)
82+
const after = mutate(before)
83+
state.formState.values = setIn(state.formState.values, name, after) || {}
84+
})
9785
const state = {
9886
formState: {
9987
values: {
100-
foo: ['one', 'two']
88+
foo: ['a', 'b', 'c']
10189
}
10290
},
10391
fields: {
@@ -113,7 +101,8 @@ describe('pop', () => {
113101
}
114102
}
115103
}
116-
const returnValue = pop(['foo'], state, { changeValue })
104+
const returnValue = pop(['foo'], state, { changeValue, getIn, setIn })
105+
const result = state.formState.values.foo
117106
expect(returnValue).toBe('c')
118107
expect(Array.isArray(result)).toBe(true)
119108
expect(result).toEqual(['a', 'b'])
@@ -161,7 +150,7 @@ describe('pop', () => {
161150
}
162151
}
163152
}
164-
const returnValue = pop(['foo'], state, { changeValue })
153+
const returnValue = pop(['foo'], state, { changeValue, getIn, setIn })
165154
expect(returnValue).toBe('d')
166155
expect(Array.isArray(state.formState.values.foo)).toBe(true)
167156
expect(state.formState.values.foo).not.toBe(array) // copied
@@ -238,7 +227,7 @@ describe('pop', () => {
238227
}
239228
}
240229
}
241-
const returnValue = pop(['foo[0]'], state, { changeValue })
230+
const returnValue = pop(['foo[0]'], state, { changeValue, getIn, setIn })
242231
expect(returnValue).toBe('d')
243232
expect(Array.isArray(state.formState.values.foo)).toBe(true)
244233
expect(state.formState.values.foo).not.toBe(array) // copied

src/remove.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ const remove: Mutator<any> = (
99
{ changeValue, getIn, setIn }: Tools<any>
1010
) => {
1111
let returnValue
12-
changeValue(state, name, (array: ?(any[])): any[] => {
13-
const copy = [...(array || [])]
12+
changeValue(state, name, (array: ?(any[])): ?(any[]) => {
13+
if (!array) {
14+
return array
15+
}
16+
17+
const copy = [...array]
1418
returnValue = copy[index]
1519
copy.splice(index, 1)
16-
return copy
20+
return copy.length > 0
21+
? copy
22+
: undefined
1723
})
1824

1925
// now we have to remove any subfields for our index,

src/remove.test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ describe('remove', () => {
4646
expect(returnValue).toBeUndefined()
4747
const op = changeValue.mock.calls[0][2]
4848
const result = op(undefined)
49-
expect(Array.isArray(result)).toBe(true)
50-
expect(result.length).toBe(0)
49+
expect(result).toBeUndefined()
5150
})
5251

5352
it('should remove value from the specified index, and return it', () => {

src/removeBatch.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ const removeBatch: Mutator<any> = (
5959
copy.splice(index, 1)
6060
}
6161

62-
return copy
62+
return copy.length > 0
63+
? copy
64+
: undefined
6365
})
6466

6567
// now we have to remove any subfields for our indexes,

src/shift.test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ describe('shift', () => {
4646
expect(returnValue).toBeUndefined()
4747
const op = changeValue.mock.calls[0][2]
4848
const result = op(undefined)
49-
expect(Array.isArray(result)).toBe(true)
50-
expect(result.length).toBe(0)
49+
expect(result).toBeUndefined()
5150
})
5251

5352
it('should remove first value from array and return it', () => {

0 commit comments

Comments
 (0)