Come velocizzare il rendering dell'applicazione React

Nov 27 2022
Fornire applicazioni velocemente è facile, fornire applicazioni veloci è molto più difficile. Ecco alcuni suggerimenti per far sì che la tua app di reazione renda più velocemente i tuoi componenti.

Fornire applicazioni velocemente è facile, fornire applicazioni veloci è molto più difficile. Ecco alcuni suggerimenti per far sì che la tua app di reazione renda più velocemente i tuoi componenti.

Mentre cerchiamo di fornire le nostre applicazioni più velocemente, spesso ci siamo dimenticati di concentrarci sulle best practice. Dopo un po 'creiamo componenti così complessi che una volta digitato un tasto i posti non hanno bisogno di essere renderizzati vengono renderizzati. Ecco la lista di cosa fare e cosa non fare.

Usa React.Memo

Di solito non vogliamo che il nostro componente esegua il rendering ogni volta che il suo genitore viene renderizzato. Possiamo dire React di non eseguire il rendering dei nostri componenti a meno che non sia necessario.

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

Non avere più di 4 oggetti di scena per componente

Avere molti oggetti di scena è sempre problematico, ogni oggetto di scena significa una variabile in più da considerare quando si tratta di prestazioni e renderà il tuo codice più difficile da leggere e mantenere. Invece di creare componenti con 10 oggetti di scena, crea 3 pezzi più piccoli con 3-4 oggetti di scena.

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

hook quando hai variabili che verranno calcolate dopo che qualcosa cambia. È utile perché se il tuo calcolo richiede tempo o dopo il calcolo otterrai diversi riferimenti di memoria per array o oggetti.

Per favore, non dimenticare che quando le tue variabili vengono modificate e le passi ai figli, il componente figli verrà ri-renderizzato.

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

Devi usare useCallback hook quando passerai la tua funzione a un componente figlio. Non dimenticare che le funzioni punteranno a un punto di memoria diverso ogni volta che i tuoi componenti vengono renderizzati. Abbiamo bisogno di mantenere in memoria il precedente callback in modo che non cambi ogni volta che facciamo qualcosa nel componente principale e provochiamo il rendering nei figli.

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

Ci siamo abituati a utilizzare questo hook per passarlo ai bambini e utilizzare le loro istanze, ma questo non è solo il caso d'uso disponibile disponibile per esso. Possiamo usarlo come una sorta di memorizzazione nella cache e meccanismo di stato senza causare il rerendering.

// 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
  }
}, [])

Non dimenticarlo mai, ogni volta che esegui il rendering dei componenti, le variabili all'interno di quel componente indicheranno il diverso indirizzo di memoria, quando si tratta di tipi primitivi come string e integer questo non causerà alcun problema ma se stai lavorando con array, oggetti e funzione (perché ogni funzione in realtà sono oggetti dietro il cofano). Vediamo alcuni esempi

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

Potresti pensare che gestire i moduli in reazione sia facile, ma lascia che ti garantisca che non lo è. Quando i tuoi moduli diventano complicati e devi fare alcune convalide, ecc., Causerai troppi rendering. Ecco perché è importante utilizzare una buona libreria. Negli ultimi due anni ho provato molte librerie diverse ma ce n'è solo una che vale la pena menzionare qui; reazione-gancio-forma.

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>
  );
}