Atraskite pažangius React Context Provider šablonus, skirtus efektyviai valdyti būseną, optimizuoti našumą ir išvengti nereikalingų pervaizdavimų jūsų programose.
React Context Provider šablonai: našumo optimizavimas ir nereikalingų pervaizdavimų išvengimas
React Context API yra galingas įrankis globaliai būsenai (state) valdyti jūsų programose. Jis leidžia dalintis duomenimis tarp komponentų, nereikalaujant rankiniu būdu perduoti „props“ kiekviename lygyje. Tačiau neteisingas Context naudojimas gali sukelti našumo problemų, ypač nereikalingų pervaizdavimų. Šiame straipsnyje nagrinėjami įvairūs Context Provider šablonai, padedantys optimizuoti našumą ir išvengti šių spąstų.
Problemos supratimas: nereikalingi pervaizdavimai
Pagal numatytuosius nustatymus, pasikeitus konteksto vertei, visi komponentai, kurie naudoja tą kontekstą, bus pervaizduojami iš naujo, net jei jie nepriklauso nuo konkrečios pasikeitusios konteksto dalies. Tai gali tapti dideliu našumo butelio kakleliu, ypač didelėse ir sudėtingose programose. Įsivaizduokite scenarijų, kai turite kontekstą, kuriame yra vartotojo informacija, temos nustatymai ir programos parinktys. Jei pasikeičia tik temos nustatymas, idealiu atveju turėtų būti pervaizduojami tik su tema susiję komponentai, o ne visa programa.
Pavyzdžiui, įsivaizduokite globalią el. prekybos programą, prieinamą keliose šalyse. Jei pasikeistų valiutos pasirinkimas (valdomas per kontekstą), nenorėtumėte, kad visas produktų katalogas būtų pervaizduojamas iš naujo – atnaujinti reikia tik kainų rodymą.
1 šablonas: Vertės memoizacija su useMemo
Paprasčiausias būdas išvengti nereikalingų pervaizdavimų yra memoizuoti konteksto vertę naudojant useMemo
. Tai užtikrina, kad konteksto vertė pasikeis tik tada, kai pasikeis jos priklausomybės.
Pavyzdys:
Tarkime, turime `UserContext`, kuris teikia vartotojo duomenis ir funkciją vartotojo profiliui atnaujinti.
import React, { createContext, useState, useMemo } from 'react';
const UserContext = createContext(null);
function UserProvider({ children }) {
const [user, setUser] = useState({
name: 'John Doe',
email: 'john.doe@example.com',
location: 'New York, USA'
});
const updateUser = (newUserData) => {
setUser(prevState => ({ ...prevState, ...newUserData }));
};
const contextValue = useMemo(() => ({
user,
updateUser,
}), [user, setUser]);
return (
{children}
);
}
export { UserContext, UserProvider };
Šiame pavyzdyje useMemo
užtikrina, kad `contextValue` pasikeis tik tada, kai pasikeis `user` būsena arba `setUser` funkcija. Jei nei viena, nei kita nepasikeičia, komponentai, naudojantys `UserContext`, nebus pervaizduojami.
Privalumai:
- Paprasta įgyvendinti.
- Apsaugo nuo pervaizdavimų, kai konteksto vertė iš tikrųjų nepasikeičia.
Trūkumai:
- Vis tiek pervaizduojama, jei bet kuri vartotojo objekto dalis pasikeičia, net jei komponentui, kuris jį naudoja, reikia tik vartotojo vardo.
- Gali tapti sudėtinga valdyti, jei konteksto vertė turi daug priklausomybių.
2 šablonas: Atsakomybių atskyrimas naudojant kelis kontekstus
Detalesnis požiūris yra padalinti kontekstą į kelis mažesnius kontekstus, kurių kiekvienas atsakingas už tam tikrą būsenos dalį. Tai sumažina pervaizdavimų apimtį ir užtikrina, kad komponentai būtų pervaizduojami tik tada, kai pasikeičia konkretūs duomenys, nuo kurių jie priklauso.
Pavyzdys:
Vietoj vieno `UserContext` galime sukurti atskirus kontekstus vartotojo duomenims ir vartotojo nustatymams.
import React, { createContext, useState } from 'react';
const UserDataContext = createContext(null);
const UserPreferencesContext = createContext(null);
function UserDataProvider({ children }) {
const [user, setUser] = useState({
name: 'John Doe',
email: 'john.doe@example.com',
location: 'New York, USA'
});
const updateUser = (newUserData) => {
setUser(prevState => ({ ...prevState, ...newUserData }));
};
return (
{children}
);
}
function UserPreferencesProvider({ children }) {
const [theme, setTheme] = useState('light');
const [language, setLanguage] = useState('en');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
{children}
);
}
export { UserDataContext, UserDataProvider, UserPreferencesContext, UserPreferencesProvider };
Dabar komponentai, kuriems reikia tik vartotojo duomenų, gali naudoti `UserDataContext`, o komponentai, kuriems reikia tik temos nustatymų, gali naudoti `UserPreferencesContext`. Temos pakeitimai nebesukels `UserDataContext` naudojančių komponentų pervaizdavimo ir atvirkščiai.
Privalumai:
- Sumažina nereikalingus pervaizdavimus izoliuojant būsenos pokyčius.
- Pagerina kodo organizavimą ir palaikymą.
Trūkumai:
- Gali lemti sudėtingesnes komponentų hierarchijas su keliais teikėjais (providers).
- Reikalauja kruopštaus planavimo, norint nustatyti, kaip padalinti kontekstą.
3 šablonas: Selektorių funkcijos su pasirinktiniais „hooks“
Šis šablonas apima pasirinktinių „hooks“ (custom hooks) kūrimą, kurie ištraukia konkrečias konteksto vertės dalis ir pervaizduoja komponentą tik tada, kai tos konkrečios dalys pasikeičia. Tai ypač naudinga, kai turite didelę konteksto vertę su daug savybių, tačiau komponentui reikia tik kelių iš jų.
Pavyzdys:
Naudodami pradinį `UserContext`, galime sukurti pasirinktinius „hooks“, kad pasirinktume konkrečias vartotojo savybes.
import React, { useContext } from 'react';
import { UserContext } from './UserContext'; // Assuming UserContext is in UserContext.js
function useUserName() {
const { user } = useContext(UserContext);
return user.name;
}
function useUserEmail() {
const { user } = useContext(UserContext);
return user.email;
}
export { useUserName, useUserEmail };
Dabar komponentas gali naudoti `useUserName`, kad būtų pervaizduotas tik pasikeitus vartotojo vardui, ir `useUserEmail`, kad būtų pervaizduotas tik pasikeitus vartotojo el. pašto adresui. Kitų vartotojo savybių (pvz., vietovės) pakeitimai nesukels pervaizdavimo.
import React from 'react';
import { useUserName, useUserEmail } from './UserHooks';
function UserProfile() {
const name = useUserName();
const email = useUserEmail();
return (
Name: {name}
Email: {email}
);
}
Privalumai:
- Smulkmeniškas pervaizdavimų valdymas.
- Sumažina nereikalingus pervaizdavimus, prenumeruojant tik konkrečias konteksto vertės dalis.
Trūkumai:
- Reikia rašyti pasirinktinius „hooks“ kiekvienai savybei, kurią norite pasirinkti.
- Gali atsirasti daugiau kodo, jei turite daug savybių.
4 šablonas: Komponento memoizacija su React.memo
React.memo
yra aukštesnės eilės komponentas (HOC), kuris memoizuoja funkcinį komponentą. Jis neleidžia komponentui būti pervaizduotam, jei jo „props“ nepasikeitė. Galite tai derinti su kontekstu, kad dar labiau optimizuotumėte našumą.
Pavyzdys:
Tarkime, turime komponentą, kuris rodo vartotojo vardą.
import React, { useContext } from 'react';
import { UserContext } from './UserContext';
function UserName() {
const { user } = useContext(UserContext);
return Name: {user.name}
;
}
export default React.memo(UserName);
Apgaubus `UserName` su `React.memo`, jis bus pervaizduojamas tik tada, jei pasikeis `user` „prop“ (netiesiogiai perduotas per kontekstą). Tačiau šiame paprastame pavyzdyje vien `React.memo` neužkirs kelio pervaizdavimui, nes visas `user` objektas vis dar perduodamas kaip „prop“. Kad jis būtų tikrai veiksmingas, jį reikia derinti su selektorių funkcijomis arba atskirais kontekstais.
Efektyvesnis pavyzdys derina `React.memo` su selektorių funkcijomis:
import React from 'react';
import { useUserName } from './UserHooks';
function UserName() {
const name = useUserName();
return Name: {name}
;
}
function areEqual(prevProps, nextProps) {
// Custom comparison function
return prevProps.name === nextProps.name;
}
export default React.memo(UserName, areEqual);
Čia `areEqual` yra pasirinktinė palyginimo funkcija, kuri tikrina, ar pasikeitė `name` „prop“. Jei nepasikeitė, komponentas nebus pervaizduojamas.
Privalumai:
- Apsaugo nuo pervaizdavimų, pagrįstų „props“ pasikeitimais.
- Gali žymiai pagerinti našumą gryniesiems funkciniams komponentams (pure functional components).
Trūkumai:
- Reikalauja atidaus „props“ pasikeitimų svarstymo.
- Gali būti mažiau efektyvus, jei komponentas gauna dažnai besikeičiančius „props“.
- Numatytasis „props“ palyginimas yra paviršutiniškas (shallow); sudėtingiems objektams gali prireikti pasirinktinės palyginimo funkcijos.
5 šablonas: Konteksto ir „reducers“ derinimas (useReducer)
Konteksto derinimas su useReducer
leidžia valdyti sudėtingą būsenos logiką ir optimizuoti pervaizdavimus. useReducer
suteikia nuspėjamą būsenos valdymo modelį ir leidžia atnaujinti būseną pagal veiksmus, sumažinant poreikį perduoti kelias nustatymo funkcijas per kontekstą.
Pavyzdys:
import React, { createContext, useReducer, useContext } from 'react';
const UserContext = createContext(null);
const initialState = {
user: {
name: 'John Doe',
email: 'john.doe@example.com',
location: 'New York, USA'
},
theme: 'light',
language: 'en'
};
const reducer = (state, action) => {
switch (action.type) {
case 'UPDATE_USER':
return { ...state, user: { ...state.user, ...action.payload } };
case 'TOGGLE_THEME':
return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
case 'SET_LANGUAGE':
return { ...state, language: action.payload };
default:
return state;
}
};
function UserProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
{children}
);
}
function useUserState() {
const { state } = useContext(UserContext);
return state.user;
}
function useUserDispatch() {
const { dispatch } = useContext(UserContext);
return dispatch;
}
export { UserContext, UserProvider, useUserState, useUserDispatch };
Dabar komponentai gali pasiekti būseną ir iškviesti veiksmus naudodami pasirinktinius „hooks“. Pavyzdžiui:
import React from 'react';
import { useUserState, useUserDispatch } from './UserContext';
function UserProfile() {
const user = useUserState();
const dispatch = useUserDispatch();
const handleUpdateName = (e) => {
dispatch({ type: 'UPDATE_USER', payload: { name: e.target.value } });
};
return (
Name: {user.name}
);
}
Šis šablonas skatina labiau struktūrizuotą požiūrį į būsenos valdymą ir gali supaprastinti sudėtingą konteksto logiką.
Privalumai:
- Centralizuotas būsenos valdymas su nuspėjamais atnaujinimais.
- Sumažina poreikį perduoti kelias nustatymo funkcijas per kontekstą.
- Pagerina kodo organizavimą ir palaikymą.
Trūkumai:
- Reikalingas
useReducer
„hook“ ir „reducer“ funkcijų supratimas. - Gali būti perteklinis paprasto būsenos valdymo scenarijams.
6 šablonas: Optimistiniai atnaujinimai
Optimistiniai atnaujinimai apima vartotojo sąsajos atnaujinimą iškart, tarsi veiksmas būtų pavykęs, dar prieš serveriui tai patvirtinant. Tai gali žymiai pagerinti vartotojo patirtį, ypač esant dideliam vėlavimui. Tačiau tai reikalauja kruopštaus galimų klaidų tvarkymo.
Pavyzdys:
Įsivaizduokite programą, kurioje vartotojai gali „patikti“ įrašams. Optimistinis atnaujinimas nedelsiant padidintų „patinka“ skaitiklį, kai vartotojas spusteli mygtuką, ir atšauktų pakeitimą, jei serverio užklausa nepavyktų.
import React, { useContext, useState } from 'react';
import { UserContext } from './UserContext';
function LikeButton({ postId }) {
const { dispatch } = useContext(UserContext);
const [isLiking, setIsLiking] = useState(false);
const handleLike = async () => {
setIsLiking(true);
// Optimistically update the like count
dispatch({ type: 'INCREMENT_LIKES', payload: { postId } });
try {
// Simulate an API call
await new Promise(resolve => setTimeout(resolve, 500));
// If the API call is successful, do nothing (the UI is already updated)
} catch (error) {
// If the API call fails, revert the optimistic update
dispatch({ type: 'DECREMENT_LIKES', payload: { postId } });
alert('Failed to like post. Please try again.');
} finally {
setIsLiking(false);
}
};
return (
);
}
Šiame pavyzdyje veiksmas `INCREMENT_LIKES` iškviečiamas nedelsiant, o vėliau atšaukiamas, jei API užklausa nepavyksta. Tai suteikia jautresnę vartotojo patirtį.
Privalumai:
- Pagerina vartotojo patirtį suteikiant greitą grįžtamąjį ryšį.
- Sumažina juntamą vėlavimą.
Trūkumai:
- Reikalingas kruopštus klaidų tvarkymas, norint atšaukti optimistinius atnaujinimus.
- Gali sukelti neatitikimų, jei klaidos nėra tinkamai tvarkomos.
Tinkamo šablono pasirinkimas
Geriausias Context Provider šablonas priklauso nuo konkrečių jūsų programos poreikių. Štai santrauka, padėsianti pasirinkti:
- Vertės memoizacija su
useMemo
: Tinka paprastoms konteksto vertėms su nedaug priklausomybių. - Atsakomybių atskyrimas naudojant kelis kontekstus: Idealiai tinka, kai jūsų kontekste yra nesusijusių būsenos dalių.
- Selektorių funkcijos su pasirinktiniais „hooks“: Geriausia didelėms konteksto vertėms, kai komponentams reikia tik kelių savybių.
- Komponento memoizacija su
React.memo
: Efektyvu gryniesiems funkciniams komponentams, kurie gauna „props“ iš konteksto. - Konteksto ir „reducers“ derinimas (
useReducer
): Tinka sudėtingai būsenos logikai ir centralizuotam būsenos valdymui. - Optimistiniai atnaujinimai: Naudinga vartotojo patirčiai pagerinti esant dideliam vėlavimui, bet reikalauja kruopštaus klaidų tvarkymo.
Papildomi patarimai konteksto našumui optimizuoti
- Venkite nereikalingų konteksto atnaujinimų: Atnaujinkite konteksto vertę tik tada, kai tai būtina.
- Naudokite nekintamas (immutable) duomenų struktūras: Nekintamumas padeda React efektyviau aptikti pakeitimus.
- Profiluokite savo programą: Naudokite React DevTools našumo butelių kakleliams nustatyti.
- Apsvarstykite alternatyvius būsenos valdymo sprendimus: Labai didelėms ir sudėtingoms programoms apsvarstykite pažangesnes būsenos valdymo bibliotekas, tokias kaip Redux, Zustand ar Jotai.
Išvada
React Context API yra galingas įrankis, tačiau svarbu jį naudoti teisingai, kad išvengtumėte našumo problemų. Suprasdami ir taikydami šiame straipsnyje aptartus Context Provider šablonus, galite efektyviai valdyti būseną, optimizuoti našumą ir kurti efektyvesnes bei jautresnes React programas. Nepamirškite išanalizuoti savo konkrečių poreikių ir pasirinkti šabloną, kuris geriausiai atitinka jūsų programos reikalavimus.
Atsižvelgdami į globalią perspektyvą, kūrėjai taip pat turėtų užtikrinti, kad būsenos valdymo sprendimai veiktų sklandžiai skirtingose laiko juostose, valiutų formatuose ir regioniniuose duomenų reikalavimuose. Pavyzdžiui, datos formatavimo funkcija kontekste turėtų būti lokalizuota pagal vartotojo pasirinkimą ar vietą, užtikrinant nuoseklų ir tikslų datų rodymą, nepriklausomai nuo to, iš kur vartotojas pasiekia programą.