paint-brush
Mejora del rendimiento de Formik cuando es lento (interfaz de usuario de material)por@josejaviasilis
8,618 lecturas
8,618 lecturas

Mejora del rendimiento de Formik cuando es lento (interfaz de usuario de material)

por Jose Javi Asilis2022/01/11
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Se muestra una forma alternativa de aumentar el rendimiento de Formik. El problema es la constante reinterpretación de la Emoción. Este método no es necesariamente exclusivo de Material-UI (aunque aquí se usa MUI como ejemplo) y funciona para ambos: V4.x y V5.x. 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`. Esto es un poco hacky, pero en su mayoría funciona.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Mejora del rendimiento de Formik cuando es lento (interfaz de usuario de material)
Jose Javi Asilis HackerNoon profile picture

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 - TL;DR

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} .

La solución

Envuelva su componente TextField con el siguiente código (Usa TypeScript):


PerformantTextField.tsx

 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.


usePropagateRef.ts

 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 />


Repositorio de GitHub

Compruébalo aquí: https://github.com/superjose/increase-formik-performance-react

Los archivos a verificar se pueden encontrar aquí: src/components/Fields/Form/PerformantTextField/