diff --git a/src/move.js b/src/move.js index c8d2454..6a481bf 100644 --- a/src/move.js +++ b/src/move.js @@ -31,32 +31,40 @@ const move: Mutator = ( // decrement all indices between from and to for (let i = from; i < to; i++) { const destKey = `${name}[${i}]${suffix}` - state.fields[destKey] = { - ...state.fields[`${name}[${i + 1}]${suffix}`], - name: destKey, - lastFieldState: undefined // clearing lastFieldState forces renotification - } + moveFieldState({ + destKey, + source: state.fields[`${name}[${i + 1}]${suffix}`] + }) } } else { // moving to a lower index // increment all indices between to and from for (let i = from; i > to; i--) { const destKey = `${name}[${i}]${suffix}` - state.fields[destKey] = { - ...state.fields[`${name}[${i - 1}]${suffix}`], - name: destKey, - lastFieldState: undefined // clearing lastFieldState forces renotification - } + moveFieldState({ + destKey, + source: state.fields[`${name}[${i - 1}]${suffix}`] + }) } } const toKey = `${name}[${to}]${suffix}` - state.fields[toKey] = { - ...backup, - name: toKey, - lastFieldState: undefined // clearing lastFieldState forces renotification - } + moveFieldState({ + destKey: toKey, + source: backup + }) } }) + + function moveFieldState({ destKey, source }) { + state.fields[destKey] = { + ...source, + name: destKey, + change: state.fields[destKey].change, // prevent functions from being overwritten + blur: state.fields[destKey].blur, + focus: state.fields[destKey].focus, + lastFieldState: undefined // clearing lastFieldState forces renotification + } + } } export default move diff --git a/src/move.test.js b/src/move.test.js index 9bd8180..7858313 100644 --- a/src/move.test.js +++ b/src/move.test.js @@ -432,4 +432,55 @@ describe('move', () => { } }) }) + + it('should preserve functions in field state', () => { + // implementation of changeValue taken directly from Final Form + const changeValue = (state, name, mutate) => { + const before = getIn(state.formState.values, name) + const after = mutate(before) + state.formState.values = setIn(state.formState.values, name, after) || {} + } + const state = { + formState: { + values: { + foo: ['apple', 'banana', 'carrot', 'date'] + } + }, + fields: { + 'foo[0]': { + name: 'foo[0]', + touched: true, + error: 'Error A', + lastFieldState: 'anything', + change: () => 'foo[0]' + }, + 'foo[1]': { + name: 'foo[1]', + touched: true, + error: 'Error B', + lastFieldState: 'anything', + change: () => 'foo[1]' + }, + 'foo[2]': { + name: 'foo[2]', + touched: false, + error: 'Error C', + lastFieldState: 'anything', + change: () => 'foo[2]' + }, + 'foo[3]': { + name: 'foo[3]', + touched: false, + error: 'Error D', + lastFieldState: 'anything', + change: () => 'foo[3]' + } + } + } + move(['foo', 0, 2], state, { changeValue }) + expect(state.fields['foo[0]'].change()).toBe('foo[0]') + expect(state.fields['foo[1]'].change()).toBe('foo[1]') + expect(state.fields['foo[2]'].change()).toBe('foo[2]') + expect(state.fields['foo[3]'].change()).toBe('foo[3]') + }) }) diff --git a/src/swap.js b/src/swap.js index 65b030f..93c24cc 100644 --- a/src/swap.js +++ b/src/swap.js @@ -29,18 +29,28 @@ const swap: Mutator = ( const aKey = aPrefix + suffix const bKey = bPrefix + suffix const fieldA = state.fields[aKey] - state.fields[aKey] = { - ...state.fields[bKey], - name: aKey, - lastFieldState: undefined // clearing lastFieldState forces renotification - } - state.fields[bKey] = { - ...fieldA, - name: bKey, - lastFieldState: undefined // clearing lastFieldState forces renotification - } + + moveFieldState({ + destKey: aKey, + source: state.fields[bKey] + }) + moveFieldState({ + destKey: bKey, + source: fieldA + }) } }) + + function moveFieldState({ destKey, source }) { + state.fields[destKey] = { + ...source, + name: destKey, + change: state.fields[destKey].change, // prevent functions from being overwritten + blur: state.fields[destKey].blur, + focus: state.fields[destKey].focus, + lastFieldState: undefined // clearing lastFieldState forces renotification + } + } } export default swap diff --git a/src/swap.test.js b/src/swap.test.js index e586d48..e1b0bc0 100644 --- a/src/swap.test.js +++ b/src/swap.test.js @@ -254,4 +254,55 @@ describe('swap', () => { } }) }) + + it('should preserve functions in field state', () => { + // implementation of changeValue taken directly from Final Form + const changeValue = (state, name, mutate) => { + const before = getIn(state.formState.values, name) + const after = mutate(before) + state.formState.values = setIn(state.formState.values, name, after) || {} + } + const state = { + formState: { + values: { + foo: ['apple', 'banana', 'carrot', 'date'] + } + }, + fields: { + 'foo[0]': { + name: 'foo[0]', + touched: true, + error: 'Error A', + lastFieldState: 'anything', + change: () => 'foo[0]' + }, + 'foo[1]': { + name: 'foo[1]', + touched: true, + error: 'Error B', + lastFieldState: 'anything', + change: () => 'foo[1]' + }, + 'foo[2]': { + name: 'foo[2]', + touched: false, + error: 'Error C', + lastFieldState: 'anything', + change: () => 'foo[2]' + }, + 'foo[3]': { + name: 'foo[3]', + touched: false, + error: 'Error D', + lastFieldState: 'anything', + change: () => 'foo[3]' + } + } + } + swap(['foo', 0, 2], state, { changeValue }) + expect(state.fields['foo[0]'].change()).toBe('foo[0]') + expect(state.fields['foo[1]'].change()).toBe('foo[1]') + expect(state.fields['foo[2]'].change()).toBe('foo[2]') + expect(state.fields['foo[3]'].change()).toBe('foo[3]') + }) })