"类型错误:在已吊销的代理上尝试非法操作"时将 Immer 与 setState 一起使用



我最近重构了我的 React 应用程序以使用 Immer。但是,在onFormValueChange使用produce会给我错误TypeError: illegal operation attempted on a revoked proxy而没有produce编写的版本工作正常。

这是我可以将相关代码简化为:


测试.tsx

import { produce } from 'immer';
import { IFormValues, TestForm } from './test_form';
interface ITestProps {}
interface ITestState {
type: string;
}
export class Test extends React.Component<ITestProps, ITestState> {
constructor(props: ITestProps) {
super(props);
this.state = {
type: '',
};
}
handleSubmit = (values: IFormValues) => {
console.log.log(values);
};
onFormValueChange = (values: IFormValues) => {
this.setState(
produce((draft: ITestState) => {
draft.type = values.type;
}),
);
};
// The Following version of the function works perfectly fine and as expected:
//
// onFormValueChange = (values: IFormValues) => {
//   this.setState({
//     type: values.type,
//   });
// };
render() {
let showField = true;
if (this.state.type === 'test') {
showField = false;
}
return (
<div>
<TestForm
submit={this.handleSubmit}
onValueChange={this.onFormValueChange}
>
<input name="type" />
</TestForm>
{showField && this.state.type}
</div>
);
}
}

test_form.tsx

import produce from 'immer';
export interface IFormValues {
[key: string]: any;
}
interface IFormProps {
submit: (values: IFormValues) => void;
onValueChange?: (values: IFormValues) => void;
}
export interface IFormState {
values: IFormValues;
}
interface IFieldProps {
value: any;
name: string;
onChange: (event: any) => void;
}
export class TestForm extends React.Component<IFormProps, IFormState> {
constructor(props: IFormProps) {
super(props);
const values: IFormValues = {};
this.state = {
values,
};
}
private handleSubmit = (event: any) => {
event.preventDefault();
const { submit } = this.props;
const { values } = this.state;
submit(values);
};
handleChange = (event: any) => {
const { name, value } = event.target;
this.setState(
produce((draft: IFormState) => {
draft.values[name] = value;
this.props.onValueChange && this.props.onValueChange(draft.values);
}),
);
};
public render() {
const { values } = this.state;
return (
<form onSubmit={this.handleSubmit} noValidate={true}>
<div>
{React.Children.map(
this.props.children,
(child: React.ReactElement<IFieldProps>) => (
<div>
{React.cloneElement(child, {
value: values[child.props.name],
onChange: this.handleChange,
})}
</div>
),
)}
<div>
<button type="submit">Submit</button>
</div>
</div>
</form>
);
}
}

警告:我从未使用过 Immer。但错误很明显:您正在尝试使用已撤销的代理。我的猜测是根本问题在这里:

this.setState(
produce((draft: IFormState) => {
draft.values[name] = value;
this.props.onValueChange && this.props.onValueChange(draft.values); // <=======
}),
);

produce中,您将draft.values传递到一个函数中,该函数将第二次调用produce并将values.type置于不同的草稿状态。我的猜测是,您不允许以这种方式将数据从原始produce调用中传递出去。(文档说">警告:请注意,草稿不应从异步过程中"泄漏"并存储在其他位置。异步过程完成后,草稿仍将被撤销。但该警告与异步生产者有关,而您的不是异步的。不过,这可能是一个更普遍的警告,它恰好在异步生产者部分。

如果是这样,则对handleChangeTestForm的更改将修复它:

this.setState(
produce((draft: IFormState) => {
draft.values[name] = value;
}),
() => {
this.props.onValueChange && this.props.onValueChange(this.state.values);
}
);

这可确保它在设置状态使用值调用onValueChange(大概此时它是一个普通对象,而不是代理)。

最新更新