Lås opp kraften til useRef i React. Utforsk ulike bruksområder, inkludert direkte DOM-tilgang, vedlikehold av muterbare verdier og optimalisering av funksjonelle komponenter.
React useRef: Mestring av mønstre for lagring av muterbare verdier
useRef er en kraftig hook i React som gir en måte å bevare verdier mellom renders uten å forårsake nye renders når disse verdiene endres. Den blir ofte assosiert med direkte tilgang til DOM-elementer, men dens kapabiliteter strekker seg langt utover det. Denne omfattende guiden vil dykke ned i de ulike bruksområdene for useRef, slik at du kan skrive mer effektiv og vedlikeholdbar React-kode.
Forståelse av useRef: Mer enn bare DOM-tilgang
I kjernen returnerer useRef et muterbart ref-objekt der .current-egenskapen initialiseres med det gitte argumentet (initialValue). Det returnerte objektet vil bestå i hele komponentens levetid. Avgjørende er at endring av .current-egenskapen ikke utløser en ny render. Dette er den viktigste forskjellen mellom useRef og useState.
Selv om tilgang til DOM-elementer er et vanlig bruksområde, utmerker useRef seg for å håndtere enhver muterbar verdi som ikke trenger å forårsake en ny render når den oppdateres. Dette gjør den uvurderlig for oppgaver som:
- Lagre tidligere prop- eller state-verdier.
- Vedlikeholde tellere eller tidtakere.
- Spore fokusstatus uten å forårsake nye renders.
- Lagre enhver muterbar verdi som må bestå på tvers av renders.
Grunnleggende bruk: Tilgang til DOM-elementer
Det mest kjente bruksområdet er å få direkte tilgang til DOM-elementer. Dette er nyttig for scenarioer der du trenger å interagere imperativt med en DOM-node, for eksempel å fokusere et input-felt, måle dets dimensjoner eller utløse animasjoner.
Eksempel: Fokusere et input-felt
Her er hvordan du kan bruke useRef for å fokusere et input-felt når en komponent mounter:
import React, { useRef, useEffect } from 'react';
function MyComponent() {
const inputRef = useRef(null);
useEffect(() => {
// Focus the input field on mount
if (inputRef.current) {
inputRef.current.focus();
}
}, []); // Empty dependency array ensures this runs only once on mount
return (
<input type="text" ref={inputRef} placeholder="Enter text" />
);
}
export default MyComponent;
Forklaring:
- Vi oppretter en ref med
useRef(null). Den initiale verdien ernullfordi input-elementet ennå ikke eksisterer når komponenten først rendres. - Vi fester ref-en til input-elementet ved hjelp av
ref-propen:ref={inputRef}. React vil automatisk setteinputRef.currenttil DOM-noden når input-elementet mounter. - Vi bruker
useEffectmed en tom avhengighetsliste ([]) for å sikre at effekten bare kjøres én gang etter at komponenten har mountet. - Inne i effekten sjekker vi om
inputRef.currenteksisterer (for å unngå feil hvis elementet ikke er tilgjengelig ennå) og kaller deretterinputRef.current.focus()for å fokusere input-feltet.
Utover DOM-tilgang: Håndtering av muterbare verdier
Den virkelige kraften til useRef ligger i dens evne til å lagre muterbare verdier som består på tvers av renders uten å utløse nye renders. Dette åpner for et bredt spekter av muligheter for å optimalisere komponentoppførsel og håndtere tilstand i funksjonelle komponenter.
Eksempel: Lagre tidligere prop- eller state-verdier
Noen ganger trenger du tilgang til den forrige verdien av en prop eller state-variabel. useRef gir en ren måte å gjøre dette på uten å utløse unødvendige nye renders.
import React, { useRef, useEffect } from 'react';
function MyComponent({ value }) {
const previousValue = useRef(value);
useEffect(() => {
// Update the ref's .current property with the current value
previousValue.current = value;
}, [value]); // Effect runs whenever the 'value' prop changes
// Now you can access the previous value using previousValue.current
return (
<div>
Current value: {value}
<br />
Previous value: {previousValue.current}
</div>
);
}
export default MyComponent;
Forklaring:
- Vi initialiserer ref-en
previousValuemed startverdien tilvalue-propen. - Vi bruker
useEffectfor å oppdaterepreviousValue.current-egenskapen hver gangvalue-propen endres. - Inne i komponenten kan vi nå få tilgang til den forrige verdien av
value-propen ved å brukepreviousValue.current.
Eksempel på bruksområde: Spore endringer i API-responser (Internasjonalt scenario)
Se for deg at du bygger et dashbord som viser valutakurser hentet fra en API. API-en kan returnere kursene i forskjellige formater eller med varierende presisjonsnivå avhengig av datakilden (f.eks. en API fra Den europeiske sentralbanken vs. en API fra en sørøstasiatisk finansinstitusjon). Du kan bruke useRef til å spore den forrige valutakursen og vise en visuell indikator (f.eks. en grønn pil opp eller en rød pil ned) for å vise om kursen har økt eller redusert siden forrige oppdatering. Dette er avgjørende for internasjonale brukere som er avhengige av disse kursene for økonomiske beslutninger.
Eksempel: Vedlikeholde tellere eller tidtakere
useRef er perfekt for å håndtere tellere eller tidtakere som ikke trenger å utløse nye renders. For eksempel kan du bruke den til å spore antall ganger en knapp har blitt klikket på eller for å implementere en enkel tidtaker.
import React, { useRef, useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const clickCount = useRef(0); // Initialize the ref with 0
const handleClick = () => {
clickCount.current++; // Increment the ref's .current property
setCount(clickCount.current); //Increment state which re-renders.
};
return (
<div>
<p>Button clicked: {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
);
}
export default MyComponent;
Forklaring:
- Vi initialiserer en ref
clickCountmed verdien 0. - I
handleClick-funksjonen inkrementerer viclickCount.current-egenskapen. Dette utløser ikke en ny render. - Vi oppdaterer også state-variabelen 'count', noe som utløser en ny render.
Eksempel: Implementere en Debounce-funksjon
Debouncing er en teknikk som brukes for å begrense hvor ofte en funksjon utføres. Den brukes ofte i søke-input-felt for å forhindre overdreven API-kall mens brukeren skriver. useRef kan brukes til å lagre timer-IDen som brukes i debounce-funksjonen.
import React, { useState, useRef, useEffect } from 'react';
function MyComponent() {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState([]);
const timerRef = useRef(null); // Store the timer ID
const handleChange = (event) => {
const newSearchTerm = event.target.value;
setSearchTerm(newSearchTerm);
// Clear the previous timer if it exists
if (timerRef.current) {
clearTimeout(timerRef.current);
}
// Set a new timer
timerRef.current = setTimeout(() => {
// Simulate an API call
fetch(`https://api.example.com/search?q=${newSearchTerm}`)
.then(response => response.json())
.then(data => setResults(data.results));
}, 300); // Debounce for 300 milliseconds
};
return (
<div>
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={handleChange}
/>
<ul>
{results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}
export default MyComponent;
Forklaring:
- Vi bruker
useReftil å lagre timer-IDen itimerRef. - I
handleChange-funksjonen tømmer vi den forrige timeren (hvis den eksisterer) medclearTimeout(timerRef.current). - Vi setter deretter en ny timer med
setTimeoutog lagrer timer-IDen itimerRef.current. - API-kallet gjøres bare etter at brukeren har sluttet å skrive i 300 millisekunder.
Hensyn til internasjonalisering: Når du implementerer debouncing med API-kall som involverer visning av informasjon på forskjellige språk, sørg for at API-en din støtter internasjonalisering og returnerer data på brukerens foretrukne språk. Vurder å bruke Accept-Language-headeren i API-forespørslene dine.
Eksempel: Spore fokusstatus uten nye renders
Du kan bruke useRef til å spore om et element har fokus uten å forårsake nye renders. Dette kan være nyttig for å style elementer basert på deres fokusstatus eller for å implementere tilpasset logikk for fokus-håndtering.
import React, { useRef, useState } from 'react';
function MyComponent() {
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef(null);
const handleFocus = () => {
setIsFocused(true);
};
const handleBlur = () => {
setIsFocused(false);
};
return (
<div>
<input
type="text"
ref={inputRef}
onFocus={handleFocus}
onBlur={handleBlur}
/>
<p>Input is focused: {isFocused ? 'Yes' : 'No'}</p>
</div>
);
}
export default MyComponent;
useRef vs. useState: Velge riktig verktøy
Det er viktig å forstå de viktigste forskjellene mellom useRef og useState for å velge riktig verktøy for jobben.
| Egenskap | useRef | useState |
|---|---|---|
| Utløser ny render | Nei | Ja |
| Formål | Lagre muterbare verdier som ikke trenger å utløse nye renders. Tilgang til DOM-elementer. | Håndtere tilstand som trenger å utløse nye renders. |
| Varighet | Består på tvers av renders. | Består på tvers av renders, men verdien oppdateres med setter-funksjonen. |
Beste praksis og vanlige fallgruver
- Ikke muter state direkte: Selv om
useReflar deg mutere verdier direkte, unngå å direkte mutere state-variabler håndtert avuseState. Bruk alltid setter-funksjonen levert avuseStatefor å oppdatere state. - Vær oppmerksom på bivirkninger: Når du bruker
useReffor å håndtere verdier som påvirker brukergrensesnittet, vær oppmerksom på potensielle bivirkninger. Sørg for at koden din oppfører seg forutsigbart og ikke introduserer uventede feil. - Ikke stol på
useReffor render-logikk: Siden endringer iuseRefikke utløser nye renders, ikke stol på verdiene direkte for å bestemme hva som skal rendres. BrukuseStatefor verdier som skal drive render-logikken. - Vurder ytelsesimplikasjoner: Selv om
useRefkan bidra til å optimalisere ytelsen ved å forhindre unødvendige nye renders, vær klar over at overdreven bruk av muterbare verdier kan gjøre koden din vanskeligere å resonnere om og feilsøke.
Avanserte bruksområder og mønstre
Bevaring av verdier på tvers av komponentinstanser
Mens `useRef` bevarer verdier på tvers av renders for en *enkelt* komponentinstans, trenger du noen ganger at en verdi består på tvers av *forskjellige* instanser av den samme komponenten. Dette krever en litt annen tilnærming, ofte ved å utnytte en variabel på modulnivå kombinert med `useRef`.
// myComponent.js
let globalCounter = 0; // Module-level variable
import React, { useRef, useEffect } from 'react';
function MyComponent() {
const counterRef = useRef(globalCounter); // Initialize with the global value
useEffect(() => {
// Update the global counter whenever the ref changes
globalCounter = counterRef.current;
}, [counterRef.current]);
const increment = () => {
counterRef.current++;
//No setState needed, so no re-render
};
return (
<div>
<p>Counter: {counterRef.current}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default MyComponent;
Viktige hensyn: Dette mønsteret introduserer en global variabel, så vær ekstremt forsiktig med potensielle bivirkninger og «race conditions», spesielt i komplekse applikasjoner. Vurder alternative tilnærminger som å bruke en context provider hvis verdien må deles mellom flere komponenter på en mer kontrollert måte.
Konklusjon: Slipp løs kraften i useRef
useRef er et allsidig verktøy i React som går langt utover bare å få tilgang til DOM-elementer. Ved å forstå dens evne til å lagre muterbare verdier uten å utløse nye renders, kan du optimalisere komponentene dine, håndtere tilstand mer effektivt og bygge mer ytelsessterke og vedlikeholdbare React-applikasjoner. Husk å bruke den med omhu og alltid vurdere de potensielle avveiningene mellom ytelse og kodeklarhet.
Ved å mestre mønstrene beskrevet i denne guiden, vil du være godt rustet til å utnytte det fulle potensialet til useRef i dine React-prosjekter, enten du bygger en enkel webapplikasjon eller et komplekst bedriftssystem. Husk å ta hensyn til internasjonalisering og tilgjengelighet når du bygger for et globalt publikum!