-
-
Notifications
You must be signed in to change notification settings - Fork 281
fix: deep clone array for initialValues #377
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ | |
| # misc | ||
| .DS_Store | ||
| .vscode | ||
| .idea/ | ||
| .idea | ||
|
|
||
| # umi | ||
| .umi | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| ## initialValues | ||
|
|
||
|
|
||
| <code src="../examples/initialValues.tsx" /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| /* eslint-disable react/prop-types */ | ||
|
|
||
| import React, { useState } from 'react'; | ||
| import Form from 'rc-field-form'; | ||
| import Input from './components/Input'; | ||
|
|
||
| const { Field, List } = Form; | ||
|
|
||
| const formValue = { | ||
| test: "test", | ||
| users: [{ first: "aaa", last: "bbb" }] | ||
| }; | ||
|
|
||
| export default () => { | ||
| const [form] = Form.useForm(); | ||
| const [show, setShow] = useState<boolean>(false); | ||
|
|
||
| return ( | ||
| <> | ||
| <button onClick={() => setShow((prev) => !prev)}>switch show</button> | ||
| {show && ( | ||
| <Form | ||
| form={form} | ||
| initialValues={formValue} | ||
| preserve={false} | ||
| onFinish={values => { | ||
| console.log('Submit:', values); | ||
| }} | ||
| > | ||
| <Field shouldUpdate> | ||
| {() => ( | ||
| <Field name="test" preserve={false}> | ||
| <Input/> | ||
| </Field> | ||
| )} | ||
| </Field> | ||
| <List name="users"> | ||
| {(fields) => ( | ||
| <> | ||
| {fields.map(({ key, name, ...restField }) => ( | ||
| <> | ||
| <Field | ||
| {...restField} | ||
| name={[name, "first"]} | ||
| rules={[ | ||
| { required: true, message: "Missing first name" } | ||
| ]} | ||
| > | ||
| <Input placeholder="First Name" /> | ||
| </Field> | ||
| <Field | ||
| {...restField} | ||
| name={[name, "last"]} | ||
| rules={[{ required: true, message: "Missing last name" }]} | ||
| > | ||
| <Input placeholder="Last Name" /> | ||
| </Field> | ||
| </> | ||
| ))} | ||
| </> | ||
| )} | ||
| </List> | ||
| </Form> | ||
| )} | ||
| </> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ import { | |
| setValue, | ||
| setValues, | ||
| } from './utils/valueUtil'; | ||
| import cloneDeep from './utils/cloneDeep'; | ||
|
|
||
| type InvalidateFieldEntity = { INVALIDATE_NAME_PATH: InternalNamePath }; | ||
|
|
||
|
|
@@ -129,11 +130,13 @@ export class FormStore { | |
| private setInitialValues = (initialValues: Store, init: boolean) => { | ||
| this.initialValues = initialValues || {}; | ||
| if (init) { | ||
| this.store = setValues({}, initialValues, this.store); | ||
| this.store = setValues({}, this.store, initialValues); | ||
| } | ||
| }; | ||
|
|
||
| private getInitialValue = (namePath: InternalNamePath) => getValue(this.initialValues, namePath); | ||
| private getInitialValue = (namePath: InternalNamePath) => { | ||
| return cloneDeep(getValue(this.initialValues, namePath)); | ||
| }; | ||
|
|
||
| private setCallbacks = (callbacks: Callbacks) => { | ||
| this.callbacks = callbacks; | ||
|
|
@@ -549,14 +552,13 @@ export class FormStore { | |
| // un-register field callback | ||
| return (isListField?: boolean, preserve?: boolean, subNamePath: InternalNamePath = []) => { | ||
| this.fieldEntities = this.fieldEntities.filter(item => item !== entity); | ||
|
|
||
| // Clean up store value if not preserve | ||
| const mergedPreserve = preserve !== undefined ? preserve : this.preserve; | ||
|
|
||
| if (mergedPreserve === false && (!isListField || subNamePath.length > 1)) { | ||
| const namePath = entity.getNamePath(); | ||
|
|
||
| const defaultValue = isListField ? undefined : getValue(this.initialValues, namePath); | ||
| const defaultValue = isListField ? undefined : this.getInitialValue(namePath); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里使用有深拷贝的方法获取 |
||
|
|
||
| if ( | ||
| namePath.length && | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| function cloneDeep(val) { | ||
| if (Array.isArray(val)) { | ||
| return cloneArrayDeep(val); | ||
| } else if (typeof val === 'object' && val !== null) { | ||
| return cloneObjectDeep(val); | ||
| } | ||
| return val; | ||
| } | ||
|
|
||
| function cloneObjectDeep(val) { | ||
| if (Object.getPrototypeOf(val) === Object.prototype) { | ||
| const res = {}; | ||
| for (const key in val) { | ||
| res[key] = cloneDeep(val[key]); | ||
| } | ||
| return res; | ||
| } | ||
| return val; | ||
| } | ||
|
|
||
| function cloneArrayDeep(val) { | ||
| return val.map(item => cloneDeep(item)); | ||
| } | ||
|
|
||
| export default cloneDeep; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ import get from 'rc-util/lib/utils/get'; | |
| import set from 'rc-util/lib/utils/set'; | ||
| import type { InternalNamePath, NamePath, Store, StoreValue, EventArgs } from '../interface'; | ||
| import { toArray } from './typeUtil'; | ||
| import cloneDeep from '../utils/cloneDeep'; | ||
|
|
||
| /** | ||
| * Convert name to internal supported format. | ||
|
|
@@ -64,7 +65,8 @@ function internalSetValues<T>(store: T, values: T): T { | |
|
|
||
| // If both are object (but target is not array), we use recursion to set deep value | ||
| const recursive = isObject(prevValue) && isObject(value); | ||
| newStore[key] = recursive ? internalSetValues(prevValue, value || {}) : value; | ||
|
|
||
| newStore[key] = recursive ? internalSetValues(prevValue, value || {}) : cloneDeep(value); // Clone deep for arrays | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里的深拷贝是针对 array 的。之前的做法是把 array 看作 object 处理,但是实际上处理逻辑并不一样,数组应该被直接替换,而不是比对每一个元素进行赋值 |
||
| }); | ||
|
|
||
| return newStore; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
修改了一下顺序,防止
initialValues被覆盖。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
是不是其实
this.store是没用的?初始化的时候this.store应该是空的。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
理论上是的,但我不太确定。
这个方法确实是组件第一次渲染时 init 才会传 true