为什么 formik-material-ui 组件的 onChange 没有被调用?

问题

formik 是一个用来构建受控组件表单的包, 而 formik-material-ui 是 formik 对于 material-ui 的扩展组件库

我发现在更新 formik 和 formik-material-ui 的版本以后,原来输入组件上的 onChange 不运行了

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
import { Field } from 'formik'
import { Select } from 'formik-material-ui'

...

<Select
name="method"
lable="method"
onChange={e => {
console.log(e.target.value)
}}
>

当选择 Select 中的选项时,console.log 并不会执行

原因

我已经找到和这个问题相关的 issue[1],但是还没有读完,这里先附链接。

https://github.com/stackworx/formik-material-ui/issues/129#issuecomment-581348716

解决方案

一项目开发人员提供了用 formik-material-ui 自定义输入组件的解决方案[2]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// https://stackworx.github.io/formik-material-ui/docs/guide/custom-component/#upper-casing-field

import MuiTextField from '@material-ui/core/TextField';
import { fieldToTextField, TextFieldProps } from 'formik-material-ui';
function UpperCasingTextField(props: TextFieldProps) {
const {
form: { setFieldValue },
field: { name },
} = props;
const onChange = React.useCallback(
event => {
const { value } = event.target;
setFieldValue(name, value ? value.toUpperCase() : '');
},
[setFieldValue, name]
);
return <MuiTextField {...fieldToTextField(props)} onChange={onChange} />;
}

(具体原理我还没有读懂,若有大佬了解,欢迎留言。

在这段源码中,TextField是可以替换成其他组件的,比如说 Select。但如果要换,fieldToTextField 也需要换成对应的fieldToSelect

如果需要传入 dispatch 或类似函数,可以通过 props 传入。具体方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 这段代码使用 Javascript,因此删除了 type

import Select from '@material-ui/core/Select';
import { Field } from 'formik'
import { fieldToSelect } from 'formik-material-ui';

function CustomSelect(props) {
const {
form: { setFieldValue },
field: { name },
funconchange,
} = props;
const onChange = React.useCallback(
event => {
const { value } = event.target;
// 在这里执行自己的代码
funconchange(value);
setFieldValue(name, value); // 仍然要保留
},
[setFieldValue, name]
);
return <MuiTextField {...fieldToSelect(props)} onChange={onChange} />;
}

...

<Field
component={CustomSelect}
name="method"
lable="method"
funconchange={value => {
console.log(value)
// something
}}
>

然而这样做还会出现一个 Warning,react 会提示 funconchange 不是一个有效的值 [3],不应该传入<div> 中。要不删掉要不传 stringnumber。关于这个问题我尝试在 onChange 内删除 props 的 funconchange 属性,

1
delete props.funconchange

还有

1
props.funconchange = undefined

无论是哪一种都会提示 props 是 read-only 对象。可能是因为函数传入的参数不可修改的原因,可能可以通过复制 props 对象解决,但我还没有尝试。

虽然以上的方法还不完善,但已经基本可以解决需求,如果有更好的方法我会继续更新,也欢迎大佬补充。

备注

文章发布日期:2020-03-09

formik 版本:2.1.4

formik-material-ui 版本:2.0.0-beta.1


  1. https://github.com/stackworx/formik-material-ui/issues/129#issuecomment-581348716 ↩︎

  2. https://stackworx.github.io/formik-material-ui/docs/guide/custom-component/#upper-casing-field ↩︎

  3. react 官网链接,待补 ↩︎