在下面的代码中,FormExample
正在被渲染。Form
是SampleTextField
的包装器,处理所有子字段的更改和状态。我正在尝试使用useMemo
,这样一个字段中的更改就不会重新呈现另一个字段。
问题是handleChange
函数是用它的state
作用域缓存的,所以如果我更改第一个字段,然后编辑第二个字段,则状态会被第一个字段的旧值覆盖。
我可以在useCallback
中添加wrappe-handleChange,但它只是对它引用的onChange产生了相同的问题。
我也尝试过将handleChange添加到useMemo的依赖数组中,但因为每次状态更改都会创建一个handleChange的新实例(因为重新渲染(,所以这与没有useMemo是一样的。
有没有一种模式可以用来解决这个问题?
import {TextField} from '@material-ui/core';
import * as React from 'react';
import {useContext, useState} from 'react';
const FormContext = React.createContext<any>({});
const Form = ({state, onChange, children}: any) => {
const handleChange = (name: any, val: any) => {
onChange({...state, [name]: val});
}
return (
<FormContext.Provider value={{state, handleChange}}>
{children}
</FormContext.Provider>
);
}
const SampleTextField = ({name}: any) => {
const {state, handleChange} = useContext(FormContext);
const value = state[name]
return React.useMemo(() => {
return (
<TextField name={name} value={value} onChange={(e: any) => handleChange(e.target.name, e.target.value)}/>
);
}, [value]);
}
const FormExample = () => {
const [state, setState] = useState({text1: "", text2: ""});
return (
<Form state={state} onChange={setState}>
<SampleTextField name="text1"/>
<br/><br/>
<SampleTextField name="text2"/>
</Form>
)
}
export default FormExample;
您可以使用useCallback
使handleChange
引用稳定。注意我是如何使用setState
的函数形式的,它接受当前状态作为第一个参数
import { TextField } from "@material-ui/core";
import * as React from "react";
import { useContext, useState, useMemo, useCallback, createContext } from "react";
const FormContext = createContext<any>({});
const Form = ({ state, onChange, children }: any) => {
const handleChange = useCallback(
(name: any, val: any) => {
onChange((state) => ({ ...state, [name]: val }));
},
[onChange]
);
const context = useMemo(
() => ({
state,
handleChange,
}),
[state, handleChange]
);
return (
<FormContext.Provider value={context}>{children}</FormContext.Provider>
);
};
const SampleTextField = ({ name }: any) => {
const { state, handleChange } = useContext(FormContext);
const value = state[name];
return useMemo(
() => (
<TextField
name={name}
value={value}
onChange={(e: any) => handleChange(e.target.name, e.target.value)}
/>
),
[value, handleChange]
);
};
const FormExample = () => {
const [state, setState] = useState({ text1: "", text2: "" });
return (
<Form state={state} onChange={setState}>
<SampleTextField name="text1" />
<br />
<br />
<SampleTextField name="text2" />
</Form>
);
};
export default FormExample;