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

Fixed myriad bugs with moving field state #35

Merged
merged 1 commit into from
Jul 17, 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
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.13.0",
"final-form": "^4.18.0",
"final-form": "^4.18.2",
"flow-bin": "^0.102.0",
"glow": "^1.2.2",
"husky": "^3.0.0",
Expand All @@ -73,7 +73,7 @@
"typescript": "^3.5.3"
},
"peerDependencies": {
"final-form": "^4.18.0"
"final-form": "^4.18.2"
},
"lint-staged": {
"*.{js*,ts*,json,md,css}": [
Expand Down
8 changes: 3 additions & 5 deletions src/insert.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import type { MutableState, Mutator, Tools } from 'final-form'
import moveFieldState from './moveFieldState'

const insert: Mutator<any> = (
[name, index, value]: any[],
Expand All @@ -14,24 +15,21 @@ const insert: Mutator<any> = (

// now we have increment any higher indexes
const pattern = new RegExp(`^${name}\\[(\\d+)\\](.*)`)
const changes = {}
const backup = { ...state.fields }
Object.keys(state.fields).forEach(key => {
const tokens = pattern.exec(key)
if (tokens) {
const fieldIndex = Number(tokens[1])
if (fieldIndex >= index) {
// inc index one higher
const incrementedKey = `${name}[${fieldIndex + 1}]${tokens[2]}`
changes[incrementedKey] = { ...state.fields[key] } // make copy of field state
changes[incrementedKey].name = incrementedKey
changes[incrementedKey].lastFieldState = undefined
moveFieldState(state, backup[key], incrementedKey)
}
if (fieldIndex === index) {
resetFieldState(key)
}
}
})
state.fields = { ...state.fields, ...changes }
}

export default insert
38 changes: 12 additions & 26 deletions src/move.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import type { MutableState, Mutator, Tools } from 'final-form'
import moveFieldState from './moveFieldState'

const move: Mutator<any> = (
[name, from, to]: any[],
Expand Down Expand Up @@ -27,43 +28,28 @@ const move: Mutator<any> = (
// decrement all indices between from and to
for (let i = from; i < to; i++) {
const destKey = `${name}[${i}]${suffix}`
moveFieldState({
destKey,
source: state.fields[`${name}[${i + 1}]${suffix}`]
})
moveFieldState(
state,
state.fields[`${name}[${i + 1}]${suffix}`],
destKey
)
}
} 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}`
moveFieldState({
destKey,
source: state.fields[`${name}[${i - 1}]${suffix}`]
})
moveFieldState(
state,
state.fields[`${name}[${i - 1}]${suffix}`],
destKey
)
}
}
const toKey = `${name}[${to}]${suffix}`
moveFieldState({
destKey: toKey,
source: backup
})
moveFieldState(state, backup, toKey)
}
})

function moveFieldState({ destKey, source }) {
state.fields[destKey] = {
...source,
name: destKey,
// prevent functions from being overwritten
// if the state.fields[destKey] does not exist, it will be created
// when that field gets registered, with its own change/blur/focus callbacks
change: state.fields[destKey] && state.fields[destKey].change,
blur: state.fields[destKey] && state.fields[destKey].blur,
focus: state.fields[destKey] && state.fields[destKey].focus,
lastFieldState: undefined // clearing lastFieldState forces renotification
}
}
}

export default move
23 changes: 23 additions & 0 deletions src/moveFieldState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @flow
import type { MutableState } from 'final-form'

function moveFieldState(
state: MutableState<any>,
source: Object,
destKey: string,
oldState: MutableState<any> = state
) {
state.fields[destKey] = {
...source,
name: destKey,
// prevent functions from being overwritten
// if the state.fields[destKey] does not exist, it will be created
// when that field gets registered, with its own change/blur/focus callbacks
change: oldState.fields[destKey] && oldState.fields[destKey].change,
blur: oldState.fields[destKey] && oldState.fields[destKey].blur,
focus: oldState.fields[destKey] && oldState.fields[destKey].focus,
lastFieldState: undefined // clearing lastFieldState forces renotification
}
}

export default moveFieldState
7 changes: 3 additions & 4 deletions src/remove.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import type { MutableState, Mutator, Tools } from 'final-form'
import moveFieldState from './moveFieldState'

const remove: Mutator<any> = (
[name, index]: any[],
Expand All @@ -17,7 +18,7 @@ const remove: Mutator<any> = (
// now we have to remove any subfields for our index,
// and decrement all higher indexes.
const pattern = new RegExp(`^${name}\\[(\\d+)\\](.*)`)
const backup = { ...state.fields }
const backup = { ...state, fields: { ...state.fields } }
Object.keys(state.fields).forEach(key => {
const tokens = pattern.exec(key)
if (tokens) {
Expand All @@ -29,9 +30,7 @@ const remove: Mutator<any> = (
// shift all higher ones down
delete state.fields[key]
const decrementedKey = `${name}[${fieldIndex - 1}]${tokens[2]}`
state.fields[decrementedKey] = backup[key]
state.fields[decrementedKey].name = decrementedKey
state.fields[decrementedKey].lastFieldState = undefined
moveFieldState(state, backup.fields[key], decrementedKey, backup)
}
}
})
Expand Down
33 changes: 33 additions & 0 deletions src/remove.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ describe('remove', () => {
const after = mutate(before)
state.formState.values = setIn(state.formState.values, name, after) || {}
}
function blur0() {}
function change0() {}
function focus0() {}
function blur1() {}
function change1() {}
function focus1() {}
function blur2() {}
function change2() {}
function focus2() {}
function blur3() {}
function change3() {}
function focus3() {}
const state = {
formState: {
values: {
Expand All @@ -68,21 +80,33 @@ describe('remove', () => {
fields: {
'foo[0]': {
name: 'foo[0]',
blur: blur0,
change: change0,
focus: focus0,
touched: true,
error: 'A Error'
},
'foo[1]': {
name: 'foo[1]',
blur: blur1,
change: change1,
focus: focus1,
touched: false,
error: 'B Error'
},
'foo[2]': {
name: 'foo[2]',
blur: blur2,
change: change2,
focus: focus2,
touched: true,
error: 'C Error'
},
'foo[3]': {
name: 'foo[3]',
blur: blur3,
change: change3,
focus: focus3,
touched: false,
error: 'D Error'
},
Expand All @@ -105,17 +129,26 @@ describe('remove', () => {
fields: {
'foo[0]': {
name: 'foo[0]',
blur: blur0,
change: change0,
focus: focus0,
touched: true,
error: 'A Error'
},
'foo[1]': {
name: 'foo[1]',
blur: blur1,
change: change1,
focus: focus1,
touched: true,
error: 'C Error',
lastFieldState: undefined
},
'foo[2]': {
name: 'foo[2]',
blur: blur2,
change: change2,
focus: focus2,
touched: false,
error: 'D Error',
lastFieldState: undefined
Expand Down
11 changes: 5 additions & 6 deletions src/removeBatch.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import type { MutableState, Mutator, Tools } from 'final-form'
import moveFieldState from './moveFieldState'

const countBelow = (array, value) =>
array.reduce((count, item) => (item < value ? count + 1 : count), 0)
Expand Down Expand Up @@ -38,7 +39,7 @@ const removeBatch: Mutator<any> = (
// now we have to remove any subfields for our indexes,
// and decrement all higher indexes.
const pattern = new RegExp(`^${name}\\[(\\d+)\\](.*)`)
const newFields = {}
const newState = { ...state, fields: {} }
Object.keys(state.fields).forEach(key => {
const tokens = pattern.exec(key)
if (tokens) {
Expand All @@ -48,15 +49,13 @@ const removeBatch: Mutator<any> = (
// shift all higher ones down
const decrementedKey = `${name}[${fieldIndex -
countBelow(sortedIndexes, fieldIndex)}]${tokens[2]}`
newFields[decrementedKey] = state.fields[key]
newFields[decrementedKey].name = decrementedKey
newFields[decrementedKey].lastFieldState = undefined
moveFieldState(newState, state.fields[key], decrementedKey, state)
}
} else {
newFields[key] = state.fields[key]
newState.fields[key] = state.fields[key]
}
})
state.fields = newFields
state.fields = newState.fields
return returnValue
}

Expand Down
Loading