Així eviteu aquest problema de rendiment amb React hooks

Els ganxos React prometen evitar la sobrecàrrega dels components de la classe alhora que proporcionen els mateixos beneficis. Per exemple, ens permeten escriure components de funció amb estat sense haver de preocupar-nos per emmagatzemar l'estat a la instància de classe.

No obstant això, escriure components amb estat amb ganxos requereix cura. Hi ha una subtil diferència entre inicialitzar l’estat al constructor d’un component de classe i inicialitzar-lo mitjançant el ganxo useState. Els desenvolupadors que ja entenen els components de la classe i veuen els ganxos simplement com a components de classe sense el material de la classe corren el risc d’escriure components que tinguin un rendiment pitjor que els components de la classe.

Aquí estic discutint una funció de useState que només s’esmenta breument a la FAQ oficial de Hooks. La comprensió detallada d’aquesta funció us ajudarà a treure el màxim partit a React Hooks. A més de llegir aquesta nota, us convido a experimentar amb React Hooks Test Stress Testing, una eina de comparació que he escrit per il·lustrar aquests detalls de ganxos.

Les opcions anteriors a React Hooks

Suposem que teniu alguns càlculs cars que només cal fer una vegada quan configureu el component i suposem que aquests càlculs depenen d’alguns accessoris. Un component funcional senzill fa molt malament aquí:

Això es tradueix en un rendiment molt baix, ja que el càlcul car es fa cada vegada que es fa.

Els components de la classe ho milloren permetent-nos realitzar una operació determinada només una vegada, per exemple al constructor:

Emmagatzemant el resultat del càlcul a la instància, en aquest cas dins l’estat local del component, podem evitar el costós càlcul per a cada representació posterior. Es pot diferenciar això comparant el component de classe i el component funcional mitjançant la meva eina de referència.

No obstant això, els components de la classe tenen els seus propis inconvenients, tal com s’esmenta als documents oficials de React Hooks. Per això es van introduir ganxos.

Una implementació ingènua amb useState

El ganxo useState es pot utilitzar per declarar una "variable d'estat" i establir-lo en un valor inicial. Es pot accedir a aquest valor en representacions posteriors. Amb això en ment, podeu provar ingenuament de fer el següent per millorar el rendiment del vostre component funcional:

Com que es tracta d’un estat que es comparteix entre representacions posteriors, el costós càlcul només es pot realitzar a la primera representació, igual que els components de la classe. Està vostè equivocat.

Per veure per què, recordeu que NaiveHooksComponent és només una funció, una funció que s’anomena cada vegada que es representa. Això significa que useState es diu cada vegada que es representa. Com funciona useState és una història complicada que no ens ha de preocupar. L’important és com s’anomena useState: s’anomena amb el valor de retorn costósCàlcul. Tot i això, només sabem quin és aquest valor de retorn si realment anomenem costósCàlcul. Com a resultat, el nostre NaiveHooksComponent està condemnat a fer el càlcul car per a cada representació, igual que el nostre anterior FunctionalComponent que no utilitzava State.

Fins ara, useState no ens ha aportat cap avantatge de rendiment, com es pot comprovar amb la meva eina de referència. (Per descomptat, la matriu que retorna useState també conté una funció que ens permet actualitzar fàcilment la variable d'estat, cosa que no és possible amb un component de funció simple.)

Tres maneres de memoritzar càlculs cars

Afortunadament, React Hooks ofereix tres opcions per manejar estats tan potents com els components de la classe.

1. useMemo

La primera opció és utilitzar el ganxo useMemo:

Com a regla general, useMemo només tornarà a fer el càlcul car si canvia el valor de l’arg. Tot i això, aquesta és només una regla general, ja que les futures versions de React poden recalcular ocasionalment el valor emmagatzemat.

Les dues opcions següents són més fiables.

2. Passar funcions a useState

La segona forma és passar una funció a useState:

Aquesta funció només es crida durant la primera representació. Això és molt útil. (Si voleu desar una funció actual a l'estat, l'heu d'incloure en una altra funció. En cas contrari, es desarà el valor de retorn de la funció en lloc de la funció en si.)

3. useRef

La tercera opció és utilitzar el ganxo useRef:

Aquest és una mica estrany, però funciona i està aprovat oficialment. useRef retorna un objecte ref modificable la clau actual del qual apunta a l'argument utilitzat per invocar useRef. Aquest objecte ref es conserva en representacions posteriors. Per tant, si establim que el corrent sigui lent com l’anterior, el càlcul car només es realitzarà una vegada.

comparació

Com podeu veure amb la meva eina de referència, les tres opcions són tan potents com el nostre component de classe inicial. Tanmateix, el comportament de useMemo pot canviar en el futur. Per tant, si voleu assegurar-vos que el càlcul car es faci només una vegada, heu d’utilitzar l’opció 2, que passa una funció a useState, o l’opció 3, que utilitza useRef.

L'elecció entre aquestes dues opcions depèn de si voleu actualitzar el resultat del costós càlcul. Penseu en la diferència entre l'opció 2 i l'opció 3 anàloga a la diferència entre desar alguna cosa en aquest estat o desar alguna cosa directament en aquest estat.