Material UI V5 Landing Page (Tomado de https://mui.com) y página de aterrizaje de Formik (Tomado de https://formik.org)
Esto le mostrará una forma alternativa (en comparación con Matt Faircliff y Julian Rachmann ) en la que puede aumentar el rendimiento de Formik cuando tiene múltiples entradas y escribir en una entrada se vuelve insoportable. La causa de este problema es CSS-In-JS y la constante representación de Emoción como lo hace Formik con cada pulsación de tecla.
Este método no es necesariamente exclusivo de Material-UI (aunque MUI se usa como ejemplo aquí, y esto funciona para ambos: V4.x y V5.x) y se puede usar con cualquier otra biblioteca de UI, ya que los conceptos son lo mismo. Cuidado, esto es hacky, pero funciona en producción.
Aquí está el repositorio de GitHub que muestra cómo usarlo.
El concepto es bastante simple: dejamos que el componente <TextField />
maneje su propio estado interno y luego propagamos el cambio a Formik con un evento onBlur
. También agregamos protecciones adicionales para permitir que funcionen las contraseñas precargadas. Además, cuando cambia dinámicamente un atributo de TextField
, como el name
, debemos tener la forma correcta de propagarlo correctamente, ya que onBlur
no se activará. O cuando cambia <Formik initialValues>
mientras tiene enableReinitialize={true}
.
Envuelva su componente TextField
con el siguiente código (Usa TypeScript):
import { TextFieldProps, TextField } from "@mui/material"; import { useField } from "formik"; import React, { memo, useEffect, useState } from "react"; import { usePropagateRef } from "./usePropagateRef"; export type PerformantTextFieldProps = Omit<TextFieldProps, "name"> & { name: string; /** * IF true, it will use the traditional method for disabling performance */ disablePerformance?: boolean; loading?: boolean; min?: number; max?: number; }; /** * This is kind of hacky solution, but it mostly works. Your mileage may vary */ export const PerformantTextField: React.FC<PerformantTextFieldProps> = memo( (props) => { const [field, meta] = useField(props.name); const error = !!meta.error && meta.touched; /** * For performance reasons (possible due to CSS in JS issues), heavy views * affect re-renders (Formik changes state in every re-render), bringing keyboard * input to its knees. To control this, we create a setState that handles the field's inner * (otherwise you wouldn't be able to type) and then propagate the change to Formik onBlur and * onFocus. */ const [fieldValue, setFieldValue] = useState<string | number>(field.value); const { disablePerformance, loading, ...otherProps } = props; usePropagateRef({ setFieldValue, name: props.name, value: field.value, }); /** * Using this useEffect guarantees us that pre-filled forms * such as passwords work. */ useEffect(() => { if (meta.touched) { return; } if (field.value !== fieldValue) { setFieldValue(field.value); } // eslint-disable-next-line }, [field.value]); const onChange = (evt: React.ChangeEvent<HTMLInputElement>) => { setFieldValue(evt.target.value); }; const onBlur = (evt: React.FocusEvent<HTMLInputElement>) => { const val = evt.target.value || ""; window.setTimeout(() => { field.onChange({ target: { name: props.name, value: props.type === "number" ? parseInt(val, 10) : val, }, }); }, 0); }; // Will set depending on the performance props const performanceProps = disablePerformance ? { ...field, value: loading ? "Loading..." : fieldValue, } : { ...field, value: loading ? "Loading..." : fieldValue, onChange, onBlur, onFocus: onBlur, }; return ( <> <TextField {...otherProps} InputProps={{ ...((props.type === "number" && { inputProps: { min: props?.min, max: props?.max }, }) || undefined), }} error={error} helperText={meta.touched && meta.error} {...performanceProps} /> </> ); } );
Agregamos un enlace de ayuda llamado usePropagateRef para permitirnos superar un caso extremo cuando el atributo de name
de TextField
cambia y es necesario cambiar los datos.
import { useEffect, useRef } from 'react'; type UsePropagateRefProps = { setFieldValue: React.Dispatch<React.SetStateAction<any>>; name: string; value: any; }; export function usePropagateRef(props: UsePropagateRefProps) { const { name, value, setFieldValue } = props; /** * This is a special useRef that is used to propagate Formik's changes * to the component (the other way around that is done). * * This needs to be done whenever the name property changes and the content of the * component remains the same. * * An example is when you have a dynamic view that changes the TextField's name attribute. * If we don't do this, the useBlur hook will overwrite the value that you left before you * changed the TextField's value. * */ const flagRef = useRef(true); useEffect(() => { if (flagRef.current) { flagRef.current = false; return; } setFieldValue(value); // eslint-disable-next-line }, [name]); }
Esto debería brindarle una experiencia mucho más fluida al usar <TextField />
Compruébalo aquí: https://github.com/superjose/increase-formik-performance-react
Los archivos a verificar se pueden encontrar aquí: src/components/Fields/Form/PerformantTextField/