Edited at

Formik: 全てのフィールドが再描写されないようにする


シナリオ

以下のようにして、FormikFieldに自作のコンポーネントCustomFieldを指定する場合。

import { Field, Form, FormikProps } from 'formik'

import * as React from 'react'

interface IFormValue {
custom: string
}

const CustomForm = React.memo<FormikProps<IFormValue>>(props => (
<Formik>
<Form>
<Field
name="custom"
component={CustomField}
setFieldValue={props.setFieldValue}
/>
</Form>
</Formik>
))


問題点

Formik (〜1.4.1)にはsetFieldValue()を呼び出してフィールドの値をセットすると、<Formik></Formik>内の全てのフィールドが再描写されてしまう問題があるようです。入力の度に全てのフィールドが再描写されないようにするには以下のようにstate管理をする必要があります。


1. stateprops.field.valueで初期化する。

interface ICustomFieldProps {

setFieldValue: (name: string, value: string) => void
}

interface ICustomFieldState {
value: string
}

class CustomField extends React.Component<ICustomFieldProps, ICustomFieldState> {
public state: ICustomFieldState = { value: this.props.field.value }
...
}


2. onChangeではstate.valueのみを更新する。

private handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {

this.setState({
value: event.currentTarget.value
})
}


3. onBlurprops.field.valueを更新する。

private handleBlur = (event: React.ChangeEvent<HTMLInputElement>) => {

this.props.setFieldValue(
this.props.field.name,
event.currentTarget.value
)
}


4. shouldComponentUpdateprops.field.valueとstate.valueを比較して値が異なる場合のみtrueを返す。

public shouldComponentUpdate(

nextProps: IFieldProps,
nextState: IFieldState
) {
return nextProps.field.value !== nextState.value
}


参照

How to avoid rerendering?