Cómo hacer que su aplicación React se renderice más rápido
Entregar aplicaciones rápidamente es fácil, entregar aplicaciones rápidas es mucho más difícil. Aquí algunos consejos para hacer que su aplicación de reacción renderice sus componentes más rápido.
Si bien tratamos de entregar nuestras aplicaciones más rápido, a menudo nos olvidamos de centrarnos en las mejores prácticas. Después de un tiempo, creamos componentes tan complejos que una vez que escribe un lugar clave no es necesario que se rendericen. Aquí la lista de qué hacer y qué no hacer.

Usar React.Memo
Por lo general, no queremos que nuestro componente se renderice cada vez que se renderiza su padre. Podemos decir React para no renderizar nuestros componentes a menos que sea necesario.
// Wrong usage
// Without React.memo this component will get rendered each time
// something causes its parent to render
const YourHeavyComponent = (name, title, onClick) => {
// Render something really heavy like whole page here
}
export default YourHeavyComponent
// Good usage
// This component will not render unless name, title or onClick changes
const YourHeavyComponent = (name, title, onClick) => {
// Render something really heavy like whole page here
}
export default React.memo(YourHeavyComponent)
// This component will not render unless title changes
const YourHeavyComponent = (name, title, onClick) => {
// Render something really heavy like whole page here
}
// This usage might better for some stations, use this second argument
// to specify which props will cause render
export default React.memo(YourHeavyComponent, (prevProps, nextProps) => {
return prevProps.title === nextProps.title
})
No tenga más de 4 accesorios por componente
Tener muchos accesorios siempre es problemático, cada accesorio significa una variable más a considerar cuando se trata de rendimiento y hará que su código sea más difícil de leer y mantener. En lugar de crear componentes con 10 accesorios, cree 3 partes más pequeñas con 3 o 4 accesorios.
// this component has too many filters
// it's not a good idea to add filters inside this component
// instead we need to create a seperate component to add filter inputs
// and we can also remove title from here
const List = (title, items, sortBy, keys, filters, onFilterChange, children, pagination) => {
// Render something really heavy like whole page here
}
// here userslist wont rendered unless we get a new list
// pagination payload won't cause rendering in filters
const [payload, setPayload] = useState()
const [list, setList] = useState([])
return <>
<Filters onFilterChange={setPayload} filters={payload.filters} />
<UserList items={list}/>
<Pagination onPaginationChange={setPayload} pagination={payload.pagination} />
</>
enganche cuando tenga variables que se calcularán después de que algo cambie. Es útil porque si su cálculo lleva tiempo o después del cálculo, obtendrá diferentes referencias de memoria para matrices u objetos.
No olvide que cuando cambie sus variables y las pase a los niños, el componente de los niños se volverá a representar.
// here we use useMemo for purely caching purposes, if we don't use useMemo
// each time this components rendered the calculation will have to re-run
const heavyCalculated = useMemo(() => doSomeHeavyCalculation(variable1), [variable])
// an example of wrong usage
// each time this components get rendered this
// styles variable will point to different memory address
// and this will cause rerender of YourHeavyComponent
const styles = {
container:{
marginLeft: left,
marginRight: right,
marginTop: top,
marginBottom: bottom,
}
}
// correct usage of useMemo
// this will cache your value and its memory point won't change
// so even if this components gets rendered your YourHeavyComponent won't be rendered again
const styles = useMemo(() => ({
container:{
marginLeft: left,
marginRight: right,
marginTop: top,
marginBottom: bottom,
}
}), [left, right, top, bottom])
Debe usar useCallback hook cuando pase su función a un componente secundario. No olvide que las funciones apuntarán a un punto de memoria diferente cada vez que se rendericen sus componentes. Necesitamos mantener la devolución de llamada anterior en la memoria para que no cambie cada vez que hagamos algo en el componente principal y provoque la representación en los elementos secundarios.
// wrong usage
// each time App components get rendered for some reason
// onClickCallback will be recreated and it will point to different memory address
// so it will cause YourHeavyComponent to re render
const onClickCallback = () => {
// do some stuff
}
return <div>
<YourHeavyComponent onClick={onClickCallback} />
</div>
// good usage of onClickCallback
// each time App components if your variable1 don't change
// onClickCallback will point to same memory point
// so UourHeavyCallback won't render.
const onClickCallback = () => {
// do some stuff
}
return <div>
<YourHeavyComponent onClick={onClickCallback} />
</div>
Nos acostumbramos a usar este enlace para pasárselo a los niños y usar sus instancias, pero este no solo es un caso de uso disponible para él. Podemos usarlo como una especie de mecanismo de almacenamiento en caché y de estado sin que se reproduzca.
// in this example we look for useRef value instead of setting
// sent reference is being used for checking if we send this message before or not
// this could hold in the state but if we put into state it will cause render
// it into state, because we don't want to re-render after we set sent.current
const sent = useRef(false)
const postMessage = useCallback(() => {
if (!sent.current) {
// make your api call here
sent.current = true
}
}, [])
Nunca olvides esto, cada vez que renderices los componentes, las variables dentro de ese componente señalarán las diferentes direcciones de memoria, cuando se trata de tipos primitivos como cadenas y enteros, esto no causará ningún problema, pero si estás trabajando con matrices, objetos y funciones. (porque cada función en realidad son objetos detrás del capó). Veamos algunos ejemplos
const InlineTest = () => {
// the usage below will cause YourHeavyComponent to render if
// something causes InlineTest component to render
return <YourHeavyComponent style={{
marginLeft: 10,
marginRight: 10
}} />
}
const style = {
marginLeft: 10,
marginRight: 10
}
// We move styles outside of component
// this way style will point the same memory address regardless of
// how many tames InlineTest gets rendered
const InlineTest = () => {
// the usage below will cause YourHeavyComponent to render if
// something causes InlineTest component to render
return <YourHeavyComponent style={style} />
}
const InlineTest = () => {
// the usage below will cause YourHeavyComponent to render if
// something causes InlineTest component to render
// because onClick will be assigned to a new function each time
return <YourHeavyComponent onClick={() => {
console.log('clicked');
}} />;
}
import {useCallback} from "react";
const InlineTest = () => {
// the usage below is correct way to do it
// using useCallback will make sure onClick function
// is the same between renders and it won't cause render in below component
const onClick = useCallback(() => {
console.log('clicked');
}, [])
return <YourHeavyComponent onClick={onClick} />;
}
Puede pensar que manejar formularios en reaccionar es fácil, pero permítame garantizarle que no lo es. Cuando sus formularios se complican y necesita hacer algunas validaciones, etc., provocará demasiados renderizados. Por eso es importante usar una buena biblioteca. El último par de años probé muchas bibliotecas diferentes, pero solo hay una que vale la pena mencionar aquí; forma de gancho de reacción.
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
return (
/* "handleSubmit" will validate your inputs before invoking "onSubmit" */
<form onSubmit={handleSubmit(onSubmit)}>
{/* register your input into the hook by invoking the "register" function */}
<input defaultValue="test" {...register("example")} />
{/* include validation with required or other standard HTML validation rules */}
<input {...register("exampleRequired", { required: true })} />
{/* errors will return when field validation fails */}
{errors.exampleRequired && <span>This field is required</span>}
<input type="submit" />
</form>
);
}