Fungua karibu msimbo (codebase) wowote wa React na utaona tabia ile ile. Mwendeshaji programu anahitaji kufuatilia thamani, hivyo anatumia useState. Unahitaji counter? useState. Thamani ya muda ya input? useState. Boolean ya kuwasha/kuzima modal? useState. Baada ya muda mfupi, component moja inakuwa na hooks kadhaa tofauti, kila moja ikisimamia sehemu ndogo ya data ambayo inaweza kuhitaji au kutohitaji kubaki wakati wa renders. Matokeo yake ni msimbo wenye kelele, re-renders za ziada, na state iliyotawanyika kama sarafu za mtaani kwenye component.

Tabia hiyo inaeleweka. useState ndio hook ya kwanza ambayo wengi wetu tunajifunza, na inafanya kazi. Lakini kufanya kazi si sawa na kufaa. Kuchukulia kila kipande cha data kama reactive state kunasababisha matatizo ambayo huonekana tu wakati component inapokuwa kubwa.

Hata kama Inabadilika, Haimaanishi Inahitaji State

Si kila variable inayobadilika kwa muda inapaswa kuwa kwenye useState. Baadhi ya thamani ni matokeo tu ya kitu kingine ambacho tayari unacho. Ikiwa unahifadhi jina kamili la mtumiaji kwenye state kwa sababu tu unaziunganisha firstName na lastName, sasa unakuwa na vyanzo viwili vya ukweli (sources of truth). Wakati firstName inapobadilika kwa sababu parent inafanya re-render, state yako ya fullName itabaki ikiwa imepitwa na wakati (stale) hadi utakapofanya effect nyingine ili kuisawazisha. Huhitaji effect ya usawazishaji. Unahitaji thamani inayotokana na nyingine (derived value).

const fullName = `${firstName} ${lastName}`;

Ihesabu wakati wa render. Ikiwa hesabu hiyo ni ngumu, itumie memoize. Lakini usipe useState yake binafsi isipokuwa kama mtumiaji anaweza kuhariri jina hilo kamili bila kutegemea sehemu nyingine.

Sheria hiyo hiyo inatumika kwa orodha zilizochujwa (filtered lists). Ikiwa unahifadhi allItems na filteredItems zote kwenye state, umedoboa kiwango cha kazi ya matengenezo. Chuja wakati wa render. Weka array ya chanzo na maandishi ya chujio (filter text) kwenye state, kisha pata orodha inayoonekana. Hii inahakikisha kuwa orodha iliyochujwa haitakuwa nje ya usawazisho na chanzo.

Baadhi ya Thamani Hazipaswi Kuchochea Re-renders Kamwe

useState ipo mahususi kwa ajili ya kuambia React kuwa kitu kimebadilika na DOM inaweza kuhitaji kuhuishwa (updating). Ikiwa thamani inabadilika lakini hakuna sehemu ya UI inayojali mabadiliko hayo, useRef ndio chombo bora zaidi.

Timers na intervals ni mfano wa kawaida. Kuhifadhi ID za setInterval kwenye state husababisha re-render kila wakati unapoanza au kusimamisha timer, hata kama mtumiaji hawezi kuona hiyo ID ya interval. Ref huhifadhi thamani hiyo bila kuarifu React. Mantiki hiyo hiyo inatumika kwa kufuatilia props za awali, kupima DOM nodes kabla ya kupaka rangi (paint), au kuhifadhi callback ya mwisho kwa custom hook. Jiulize: je, thamani hii inahitaji kuonekana kwenye skrini? Ikiwa jibu ni hapana, pengine haihitaji useState.

Nodi za DOM zenyewe pia zinapaswa kuwa kwenye refs. Ingawa unaweza kuhifadhi DOM element kwenye state, kufanya hivyo huchochea re-render baada ya ref callback kukamilika. Katika hali nyingi, unahitaji tu node hiyo kwa ajili ya njia ya imperative au upimaji, si kwa ajili ya kuirender tofauti.

Mtego wa Boolean

Masuala yanayohusiana na UI huwa yanatawanyika wakati kila flag inapata hook yake binafsi. Unaona components zenye isLoading, isError, na isSuccess zimefafanuliwa kama booleans tatu tofauti. Tatizo ni kwamba hali hizi tatu si huru. Ikiwa isLoading na isSuccess zote ni true, UI yako inakuwa katika hali isiyowezekana, lakini TypeScript na React zitakuacha uirender hivyohivyo.

Kuunganisha state zinazohusiana huzuia mchanganyiko huu usiofaa. Badala ya booleans tatu, fuatilia string moja ya hali (status string): 'idle', 'loading', 'success', au 'error'. Ni moja tu inayoweza kuwa hai kwa wakati mmoja, jambo ambalo huondoa hali zisizowezekana katika kiwango cha aina (type level). Ikiwa data ni tata zaidi, object yenye discriminated union inafanya mambo kuwa safi zaidi. Unapojikuta unasasisha useState kadhaa ndani ya event handler moja, hiyo ni ishara kwamba thamani hizo zinapaswa kuwa pamoja.

Tumia useReducer, Badala ya useState Nyingine

Kuna hatua ambapo updates za state zinakuwa kama mchezo wa whack-a-mole. Unaita setA, kisha setB, kisha kwa masharti unaita setC, yote ndani ya function moja. Mwendeshaji programu mwingine atakayesoma msimbo huo lazima afuatilie mfuatano huo ili kuelewa kile ambacho component inafanya hasa.

useReducer inang'ara hapa. Haiachili useState kwa sababu ni ya hali ya juu zaidi; inachukua nafasi ya useState kwa sababu mantiki inahitaji hivyo. Reducer huweka pamoja jinsi state inavyobadilika. Badala ya kutawanya amri (imperatives) kwenye event handlers, unatuma nia (intention): dispatch({ type: 'submitted' }). Reducer huamua jinsi state inayofuata itakavyokuwa. Hii inafanya upimaji (testing) kuwa rahisi sana, kwa sababu mantiki yako ya state ni pure function. Pia inafanya ufuatiliaji wa makosa (debugging) kuwa rahisi, kwa sababu kila mabadiliko huacha kitendo kinachoweza kufuatiliwa.

Huhitaji Redux ili kuhalalisha matumizi ya reducer. Ikiwa una variable tatu au zaidi za state zinazosasishwa kwa pamoja, au ikiwa state yako inayofuata inategemea sana ile ya awali, reducer inarahisisha component kwa kiasi kikubwa.

Mahali ambapo State inapatikana hasa

Wakati mwingine tatizo si jinsi unavyohifadhi state, bali ni mahali ilipo. Kosa la kawaida ni kuhamishia state kwenye parent kwa sababu tu inaweza kuhitajika kwingineko. Ikiwa leaf component moja tu inatumia sehemu ya state, iache hapo. Hii ni colocation, na inapunguza athari za mabadiliko. Usifanye parent ifanye re-render kwa sababu child imefunguka