Skip to content
This repository was archived by the owner on Aug 23, 2022. It is now read-only.

Commit ecade1d

Browse files
committed
Properly rearranging fields in an array after being removed in form reducer. Fixes #174
1 parent b1c53f1 commit ecade1d

File tree

2 files changed

+77
-9
lines changed

2 files changed

+77
-9
lines changed

src/reducers/form-reducer.js

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const initialFieldState = {
2828
valid: true,
2929
validating: false,
3030
viewValue: null,
31+
array: false,
3132
validity: {},
3233
errors: {},
3334
};
@@ -77,7 +78,7 @@ function setInField(state, localPath, props) {
7778
return icepick.assign(state, props);
7879
}
7980

80-
const field = _get(state, ['fields', localPath.join('.')], initialFieldState);
81+
const field = getField(state, localPath);
8182

8283
return icepick.setIn(
8384
state,
@@ -141,6 +142,7 @@ function _createFormReducer(model, initialState) {
141142
return setField(state, localPath, {
142143
blur: false, // will be deprecated
143144
focus: true,
145+
array: Array.isArray(action.value),
144146
});
145147

146148
case actionTypes.CHANGE: {
@@ -149,25 +151,51 @@ function _createFormReducer(model, initialState) {
149151
let setFieldDirtyState = setField(state, localPath, {
150152
dirty: true, // will be deprecated
151153
pristine: false,
154+
value: action.value,
152155
});
153156

154-
const removeKeys = action.removeKeys
155-
? Object.keys(state.fields).filter((fieldKey) => {
157+
if (action.removeKeys) {
158+
const persistKeys = [];
159+
160+
const removeKeys = Object.keys(state.fields).filter((fieldKey) => {
161+
const localStringPath = localPath.join('.');
162+
156163
for (const removeKey of action.removeKeys) {
157-
const removeKeyPath = localPath.concat(removeKey).join('.');
164+
const removeKeyPath = `${localStringPath}.${removeKey}`;
158165
if (startsWith(fieldKey, removeKeyPath)) return true;
159166
}
160167

168+
if (startsWith(fieldKey, `${localStringPath}.`)) {
169+
persistKeys.push(fieldKey);
170+
}
171+
161172
return false;
162-
})
163-
: false;
173+
});
164174

165-
if (removeKeys) {
166175
removeKeys.forEach((removeKey) => {
176+
setFieldDirtyState = icepick.updateIn(
177+
setFieldDirtyState,
178+
['fields'],
179+
(field) => icepick.dissoc(field, removeKey));
180+
});
181+
182+
persistKeys.forEach((persistKey, index) => {
183+
const newPersistKeyPath = toPath(persistKey);
184+
newPersistKeyPath[localPath.length] = index;
185+
186+
const persistField = getField(state, persistKey);
187+
188+
// Update field to new key
167189
setFieldDirtyState = setInField(
168190
setFieldDirtyState,
169-
toPath(removeKey),
170-
initialFieldState);
191+
newPersistKeyPath,
192+
persistField);
193+
194+
// Remove old key
195+
setFieldDirtyState = icepick.updateIn(
196+
setFieldDirtyState,
197+
['fields'],
198+
(field) => icepick.dissoc(field, persistKey));
171199
});
172200
}
173201

test/form-reducer-spec.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,44 @@ describe('formReducer()', () => {
134134

135135
actions.remove('test.items', 1)(dispatch, getState);
136136
});
137+
138+
it('should clean after itself when a field is removed', (done) => {
139+
const reducer = formReducer('test', {
140+
items: [
141+
{ name: 'item1' },
142+
{ name: 'item2' },
143+
],
144+
});
145+
146+
const validItem = reducer(
147+
undefined,
148+
actions.setValidity('test.items[0].name', true));
149+
const invalidItem = reducer(
150+
validItem,
151+
actions.setValidity('test.items[1].name', false));
152+
153+
assert.isFalse(invalidItem.valid, 'form should be invalid');
154+
155+
let removedState;
156+
157+
const dispatch = action => {
158+
removedState = reducer(invalidItem, action);
159+
assert.isFalse(removedState.valid);
160+
assert.isUndefined(removedState.fields['items.1.name']);
161+
};
162+
163+
const getState = () => invalidItem;
164+
165+
actions.remove('test.items', 0)(dispatch, getState);
166+
167+
const dispatch2 = action => {
168+
const removedState2 = reducer(removedState, action);
169+
assert.isTrue(removedState2.valid);
170+
done();
171+
};
172+
173+
const getState2 = () => removedState;
174+
175+
actions.remove('test.items', 0)(dispatch2, getState2);
176+
});
137177
});

0 commit comments

Comments
 (0)