Frigør potentialet i useRef i React. Udforsk forskellige anvendelser, herunder direkte DOM-adgang, vedligeholdelse af muterbare værdier og optimering af funktionelle komponenters adfærd.
React useRef: Mestring af mønstre for lagring af muterbare værdier
useRef er en kraftfuld hook i React, der giver en måde at bevare værdier mellem renders uden at forårsage re-renders, når disse værdier ændres. Den er ofte forbundet med direkte adgang til DOM-elementer, men dens muligheder strækker sig langt ud over det. Denne omfattende guide vil dykke ned i de forskellige anvendelser af useRef, så du kan skrive mere effektiv og vedligeholdelsesvenlig React-kode.
ForstĂĄelse af useRef: Mere end bare DOM-adgang
I sin kerne returnerer useRef et muterbart ref-objekt, hvis .current-egenskab initialiseres med det overførte argument (initialValue). Det returnerede objekt vil bestå i hele komponentens levetid. Afgørende er, at ændring af .current-egenskaben ikke udløser en re-render. Dette er den vigtigste forskel mellem useRef og useState.
Selvom adgang til DOM-elementer er en almindelig anvendelse, excellerer useRef i at håndtere enhver muterbar værdi, der ikke behøver at forårsage en re-render, når den opdateres. Dette gør den uvurderlig til opgaver som:
- Lagring af tidligere prop- eller state-værdier.
- Vedligeholdelse af tællere eller timere.
- Sporing af fokustilstand uden at forĂĄrsage re-renders.
- Lagring af enhver muterbar værdi, der skal bestå på tværs af renders.
Grundlæggende brug: Adgang til DOM-elementer
Den mest kendte anvendelse er direkte adgang til DOM-elementer. Dette er nyttigt i scenarier, hvor du har brug for at interagere imperativt med en DOM-node, såsom at fokusere et inputfelt, måle dets dimensioner eller udløse animationer.
Eksempel: Fokus pĂĄ et inputfelt
Her er, hvordan du kan bruge useRef til at fokusere et inputfelt, 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 opretter en ref ved hjælp af
useRef(null). Den indledende værdi ernull, fordi input-elementet endnu ikke eksisterer, når komponenten oprindeligt renderes. - Vi tilknytter ref'en til input-elementet ved hjælp af
ref-proppen:ref={inputRef}. React vil automatisk sætteinputRef.currenttil DOM-noden, når input-elementet er mountet. - Vi bruger
useEffectmed et tomt dependency array ([]) for at sikre, at effekten kun kører én gang, efter at komponenten er mountet. - Indeni effekten tjekker vi, om
inputRef.currenteksisterer (for at undgå fejl, hvis elementet endnu ikke er tilgængeligt), og kalder derefterinputRef.current.focus()for at fokusere inputfeltet.
Ud over DOM-adgang: Håndtering af muterbare værdier
Den virkelige styrke ved useRef ligger i dens evne til at lagre muterbare værdier, der består på tværs af renders uden at udløse re-renders. Dette åbner op for en bred vifte af muligheder for at optimere komponentadfærd og håndtere state i funktionelle komponenter.
Eksempel: Lagring af tidligere prop- eller state-værdier
Nogle gange har du brug for at tilgå den tidligere værdi af en prop eller en state-variabel. useRef giver en ren måde at gøre dette på uden at udløse unødvendige re-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 den indledende værdi afvalue-proppen. - Vi bruger
useEffecttil at opdaterepreviousValue.current-egenskaben, hver gangvalue-proppen ændres. - Indeni komponenten kan vi nu tilgå den tidligere værdi af
value-proppen ved hjælp afpreviousValue.current.
Anvendelseseksempel: Sporing af ændringer i API-svar (Internationalt scenarie)
Forestil dig, at du bygger et dashboard, der viser valutakurser hentet fra et API. API'et kan returnere kurserne i forskellige formater eller med varierende præcisionsniveauer afhængigt af datakilden (f.eks. et API fra Den Europæiske Centralbank vs. et API fra en sydøstasiatisk finansiel institution). Du kan bruge useRef til at spore den tidligere valutakurs og vise en visuel indikator (f.eks. en grøn pil op eller en rød pil ned) for at vise, om kursen er steget eller faldet siden sidste opdatering. Dette er afgørende for internationale brugere, der stoler på disse kurser for finansielle beslutninger.
Eksempel: Vedligeholdelse af tællere eller timere
useRef er perfekt til at håndtere tællere eller timere, der ikke behøver at udløse re-renders. For eksempel kan du bruge den til at spore antallet af gange, en knap er blevet klikket på, eller til at implementere en simpel timer.
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 værdien 0. - I
handleClick-funktionen inkrementerer viclickCount.current-egenskaben. Dette udløser ikke en re-render. - Vi opdaterer også state 'count', hvilket udløser en re-render.
Eksempel: Implementering af en Debounce-funktion
Debouncing er en teknik, der bruges til at begrænse, hvor ofte en funktion udføres. Den bruges almindeligt i søgefelter for at forhindre overdrevne API-kald, mens brugeren skriver. useRef kan bruges til at gemme det timer-ID, der bruges i debounce-funktionen.
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 bruger
useReftil at gemme timer-ID'et itimerRef. - I
handleChange-funktionen rydder vi den forrige timer (hvis den eksisterer) ved hjælp afclearTimeout(timerRef.current). - Vi sætter derefter en ny timer ved hjælp af
setTimeoutog gemmer timer-ID'et itimerRef.current. - API-kaldet foretages kun, efter at brugeren er stoppet med at skrive i 300 millisekunder.
Overvejelser vedrørende internationalisering: Når du implementerer debouncing med API-kald, der involverer visning af information på forskellige sprog, skal du sikre dig, at dit API understøtter internationalisering og returnerer data på brugerens foretrukne sprog. Overvej at bruge Accept-Language-headeren i dine API-anmodninger.
Eksempel: Sporing af fokustilstand uden re-renders
Du kan bruge useRef til at spore, om et element har fokus, uden at forårsage re-renders. Dette kan være nyttigt til at style elementer baseret på deres fokustilstand eller til at implementere tilpasset logik for fokusstyring.
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: Vælg det rette værktøj
Det er vigtigt at forstå de vigtigste forskelle mellem useRef og useState for at vælge det rette værktøj til opgaven.
| Funktion | useRef | useState |
|---|---|---|
| Udløser re-render | Nej | Ja |
| Formål | Lagring af muterbare værdier, der ikke behøver at udløse re-renders. Adgang til DOM-elementer. | Håndtering af state, der skal udløse re-renders. |
| Beståenhed | Består på tværs af re-renders. | Består på tværs af re-renders, men værdien opdateres ved hjælp af setter-funktionen. |
Bedste praksis og almindelige faldgruber
- Mutér ikke state direkte: Selvom
useRefgiver dig mulighed for at mutere værdier direkte, bør du undgå at mutere state-variabler, der styres afuseState, direkte. Brug altid den setter-funktion, somuseStatestiller til rådighed, for at opdatere state. - Vær opmærksom på bivirkninger: Når du bruger
useReftil at håndtere værdier, der påvirker UI'en, skal du være opmærksom på potentielle bivirkninger. Sørg for, at din kode opfører sig forudsigeligt og ikke introducerer uventede fejl. - Stol ikke på
useReftil renderingslogik: Da ændringer iuseRefikke udløser re-renders, skal du ikke stole direkte på dens værdier for at bestemme, hvad der skal renderes. BruguseStatetil værdier, der skal drive renderingslogik. - Overvej ydeevnekonsekvenser: Selvom
useRefkan hjælpe med at optimere ydeevnen ved at forhindre unødvendige re-renders, skal du være opmærksom på, at overdreven brug af muterbare værdier kan gøre din kode sværere at ræsonnere om og debugge.
Avancerede anvendelser og mønstre
Bevarelse af værdier på tværs af komponentinstanser
Mens `useRef` bevarer værdier på tværs af renders for en *enkelt* komponentinstans, har du nogle gange brug for, at en værdi består på tværs af *forskellige* instanser af den samme komponent. Dette kræver en lidt anden tilgang, ofte ved at udnytte en variabel på modulniveau kombineret 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;
Vigtige overvejelser: Dette mønster introducerer en global variabel, så vær yderst forsigtig med potentielle bivirkninger og race conditions, især i komplekse applikationer. Overvej alternative tilgange som at bruge en context provider, hvis værdien skal deles mellem flere komponenter på en mere kontrolleret måde.
Konklusion: Frigørelse af kraften i useRef
useRef er et alsidigt værktøj i React, der rækker langt ud over blot at tilgå DOM-elementer. Ved at forstå dens evne til at lagre muterbare værdier uden at udløse re-renders, kan du optimere dine komponenter, håndtere state mere effektivt og bygge mere performante og vedligeholdelsesvenlige React-applikationer. Husk at bruge den med omtanke og altid overveje de potentielle kompromiser mellem ydeevne og kodeklarhed.
Ved at mestre de mønstre, der er beskrevet i denne guide, vil du være godt rustet til at udnytte det fulde potentiale af useRef i dine React-projekter, uanset om du bygger en simpel webapplikation eller et komplekst virksomhedssystem. Husk at overveje internationalisering og tilgængelighed, når du bygger til et globalt publikum!