已创建表单组件
用法
<Form
initialValues={{
title: '',
name: ''
}}
onSubmit={formSubmit}
>
<Form.Field
name="title"
>
<Input placeholder="Наименование" />
</Form.Field>
<Form.Field
name="name"
>
<Input placeholder="Еще поле" />
</Form.Field>
<Button>Сохранить</Button>
</Form>
表单组件
import { FormField } from './FormField';
import { FormProps, FormContextT } from './../../../types/form';
import './style.scss';
export const FormContext = React.createContext<FormContextT>({
formData: {},
handleFieldChange: () => {}
});
export const Form = (props: FormProps): JSX.Element => {
const { children, initialValues, className, onSubmit } = props;
const [ formData, setFormData] = useState(initialValues);
const handlerSubmit = (e: React.SyntheticEvent): void => {
e.preventDefault();
onSubmit();
}
const handleFieldChange = (name: string, value: string) => {
setFormData({
...formData,
[name]: value
});
}
const context:FormContextT = {
formData,
handleFieldChange
}
return (
<FormContext.Provider value={context}>
<form
onSubmit={handlerSubmit}
>
{children}
</form>
</FormContext.Provider>
)
}
Form.Field = FormField
FormField组件
import React, { useContext } from 'react';
import { FormContext } from './Form';
import { FormFieldProps } from './../../../types/form';
export const FormField = (props: FormFieldProps): JSX.Element => {
const { name, children } = props;
const formContext = useContext(FormContext);
const { formData, handleFieldChange } = formContext;
const fieldChange = (e: React.ChangeEvent<HTMLInputElement>) => {
handleFieldChange(name, e.target.value);
if('onChange' in children.props) {
children.props.onChange(e);
}
}
console.log('re-render: ' + name);
return (
React.cloneElement(children, {
name,
onChange: fieldChange,
value: formData[name]
})
);
}
输入组件
export const Input = (props: PropsTypes) => {
const { className, value, name, placeholder, disabled, onChange, type = 'text' } = props;
console.log('re-render-input: ' + name);
return (
<input
type={type}
name={name!}
value={value!}
placeholder={placeholder!}
disabled={disabled!}
onChange={onChange!}
/>
)
}
所有表单域值都以{field:value},并通过React Context传递给FormField。问题是,当字段值被覆盖时,父组件中的formData会被更新,所有子FormField都会被重新渲染。告诉我如何使它只被更改的子项被重新渲染。
已尝试使用UseMemo钩子,但无法正确应用。
1条答案
按热度按时间m1m5dgzv1#
这里有几样东西
FormField
使用的是整个表单数据,所以即使你找到了一种方法,只重新呈现那些数据发生了变化的组件,它们仍然会重新呈现--它们只需要获取自己的值。1.如果父组件被重新渲染,那么它的所有子组件都被重新渲染。你可以用
React.memo
Package 它们,但是记忆化是有代价的。useMemo
中,否则上下文值将在每次组件重新呈现时因任何原因而改变。React.memo
在这里没有帮助- docs1.为什么决定使用context?字段是表单的直接子项。
1.为什么它们不重新渲染很重要?它们有很多吗?它们的渲染函数中有很多逻辑吗?如果没有,就让它们重新渲染(你可以改变输入,这样它只会保存模糊的值,而不是每个新字母)
可能的解决方案(假设字段不会重新呈现很重要):
1.不使用上下文,只将字段值传递给表单字段,并使用
React.memo
Package 字段。1.使用表单值创建一个小型外部存储区,并仅公开一个API(
useValue(fieldId)
和setValue(fieldId, value)
)。here是此解决方案的沙箱