Tapahtumien erottaminen Efekteistä
Tapahtumankäsittelijät suoritetaan uudelleen ainoastaan kun suoritat saman vuorovaikutuksen uudelleen. Toisin kuin Tapahtumankäsittelijät, Efektit synkronoituvat jos jokin arvo jota ne luki, kuten propsi tai tilamuuttuja, on muuttunut viimeisestä renderöinnistä. Joskus haluat myös sekoituksen molemmista käyttäytymisistä: Efekti joka suoritetaan uudelleen vastauksena joihinkin arvoihin mutta ei toisiin. Tällä sivulla opit miten se tehdään.
Tulet oppimaan
- Miten valita Tapahtumankäsittelijän ja Efektin välillä
- Miksi Efektit ovat reaktiivisia ja Tapahtumankäsittelijät eivät
- Mitä tehdä kun haluat osan Efektin koodista ei reaktiivisen
- Mitä Efekektitapahtumat ovat ja miten erottaa ne Efekteistä
- Miten lukea viimeisin propsin ja tilan arvo Efekteistä käyttäen Efektitapahtumia
Valinta tapahtumankäsittelijöiden ja Efektien välillä
Ensiksi kertaus tapahtumankäsittelijöiden ja Efektien välillä.
Kuvittele, että olet toteuttamassa chat-huoneen komponenttia. Vaatimuksesi näyttävät tältä:
- Komponenttisi tulisi automaattisesti yhdistää valittuun chat-huoneeseen.
- Kun painat “Lähetä” painiketta, sen tulisi lähettää viesti chattiin.
Sanotaan, että olet jo toteuttanut koodin niille, mutta et ole varma minne laittaa sen. Pitäisikö sinun käyttää tapahtumankäsittelijöitä vai Efektejä? Joka kerta kun sinun täytyy vastata tähän kysymykseen, harkitse miksi koodi täytyy suorittaa.
Tapahtumankäsittelijät suoritetaan vastauksena tiettyihin vuorovaikutuksiin
Käyttäjän näkökulmasta, viestin lähettäminen tapahtuu koska tietty “Lähetä” painike painettiin. Käyttäjä suuttuu jos lähetät heidän viestinsä mihin tahansa muuhun aikaan tai mistä tahansa muusta syystä. Tämän takia viestin lähettäminen tulisi olla tapahtumankäsittelijä. Tapahtumankäsittelijät antavat sinun käsitellä tiettyjä vuorovaikutuksia:
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
// ...
function handleSendClick() {
sendMessage(message);
}
// ...
return (
<>
<input value={message} onChange={e => setMessage(e.target.value)} />
<button onClick={handleSendClick}>Lähetä</button>;
</>
);
}
Tapahtumankäsittelijällä voit olla varma, että sendMessage(message)
suoritetaan vain jos käyttäjä painaa painiketta.
Efektit suoritetaan kun synkronointi on tarpeen
Muista, että sinun täytyy myös pitää komponentti yhdistettynä chat-huoneeseen. Mihin se koodi laitetaan?
Mikään tietty vuorovaikutus ei ole syy miksi tämä koodi suoritetaan. Sillä ei ole väliä miten tai miksi käyttäjä siirtyi chat-huone -sivulle. Nyt kun ne katsovat sitä ja voivat olla vuorovaikutuksessa sen kanssa, komponentin täytyy pysyä yhteydessä valittuun chat-palvelimeen. Vaikka chat-huone komponenetti olisi sovelluksen aloitusruutu, ja käyttäjä ei ole suorittanut mitään vuorovaikutuksia, sinun täytyy silti yhdistää. Tämän takia se on Efekti:
function ChatRoom({ roomId }) {
// ...
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}
Tällä koodilla, voit olla varma että on aina aktiivinen yhteys valittuun chat-palvelimeen, riippumatta käyttäjän suorittamista vuorovaikutuksista. Olipa käyttäjä vain avannut sovelluksesi, valinnut eri huoneen, tai navigoinut toiselle sivulle ja takaisin, Efektisi varmistaa että komponentti pysyy synkronoituna valittuun huoneeseen, ja yhdistää uudelleen aina kun se on tarpeen.
import { useState, useEffect } from 'react'; import { createConnection, sendMessage } from './chat.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); }, [roomId]); function handleSendClick() { sendMessage(message); } return ( <> <h1>Welcome to the {roomId} room!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> <button onClick={handleSendClick}>Lähetä</button> </> ); } export default function App() { const [roomId, setRoomId] = useState('general'); const [show, setShow] = useState(false); return ( <> <label> Choose the chat room:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">general</option> <option value="travel">travel</option> <option value="music">music</option> </select> </label> <button onClick={() => setShow(!show)}> {show ? 'Close chat' : 'Open chat'} </button> {show && <hr />} {show && <ChatRoom roomId={roomId} />} </> ); }
Reaktiiviset arvot ja reaktiivinen logiikka
Intuitiivisesti, voisit sanoa, että tapahtumankäsittelijät ovat aina käynnistetty “manuaalisesti”, esimerkiksi painamalla nappia. Efektit, ovat toisaalta “automaattisia”: ne suoritetaan ja suoritetaan uudelleen niin usein kuin on tarpeen pysyäkseen synkronoituna.
On tarkempi tapa ajatella tätä.
Propsit, tila, ja komponentissa määritellyt muuttujat ovat reaktiivisia arvoja. Tässä esimerkissä, serverUrl
ei ole reaktiivinen arvo, mutta roomId
ja message
ovat. Ne osallistuvat renderöinnin datavirtaan:
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
// ...
}
Tämän kaltaiset reaktiiviset arvot voivat muuttua renderöinnin yhteydessä. Esimerkiksi, käyttäjä saattaa muokata message
tilaa tai valita eri roomId
:n pudotusvalikosta. Tapahtumankäsittelijät ja Efektit reagoivat muutoksiin eri tavalla:
- Tapahtumankäsittelijöissä oleva logiikka ei ole reaktiivista. Sitä ei suoriteta ellei käyttäjä suorita samaa vuorovaikutusta (esim. klikkausta) uudelleen. Tapahtumankäsittelijät voivat lukea reaktiivisia arvoja ilman että ne “reagoivat” niiden muutoksiin.
- Efekteissa oleva logiikka on reaktiivsta. Jos Efektisi lukee reaktiivista arvoa, sinun täytyy määritellä se riippuvuudeksi. Sitte, jos uudelleenrenderöinti aiheuttaa arvon muuttumisen, React suorittaa Efektisi logiikan uudelleen uudella arvolla.
Käydään edellinen esimerkki läpi uudelleen eron havainnollistamiseksi.
Logiikka tapahtumankäsittelijöissä ei ole reaktiivista
Katso tätä koodiriviä. Pitäisikö tämän logiikan olla reaktiivista vai ei?
// ...
sendMessage(message);
// ...
Käyttäjän näkökulmasta, muutos message
:en ei tarkoita, että he haluavat lähettää viestin. Se tarkoittaa vain, että käyttäjä kirjoittaa. Toisin sanoen, logiikka joka lähettää viestin ei tulisi olla reaktiivista. Sitä ei pitäisi suorittaa uudelleen vain koska reaktiivinen arvo on muuttunut. Tämän takia se kuuluu tapahtumankäsittelijään:
function handleSendClick() {
sendMessage(message);
}
Tapahtumankäsittelijät eivät ole reaktiivisia, joten sendMessage(message)
suoritetaan ainoastaan kun käyttäjä painaa Lähetä painiketta.
Logiikka Efekteissa on reaktiivista
Nyt palataan näihin riveihin:
// ...
const connection = createConnection(serverUrl, roomId);
connection.connect();
// ...
Käyttäjän näkökulmasta, muutos roomId
tilaan tarkoittaa että he haluavat yhdistää eri huoneeseen. Toisin sanoen, logiikka huoneen yhdistämiseen tulisi olla reaktiivista. Haluat, että nämä koodirivit “pysyvät mukana” reaktiivisessa arvossa, ja suoritetaan uudelleen jos arvo on eri. Tämän takia se kuuluu Efektiin:
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect()
};
}, [roomId]);
Efektit ovat reaktiivisia, joten createConnection(serverUrl, roomId)
ja connection.connect()
suoritetaan jokaiselle eri roomId
arvolle. Efektisi pitää chat-yhteyden synkronoituna valittuun huoneeseen.
Ei-reaktiivisen logiikan irroittaminen Efekteista
Asioista tulee hankalampia kun haluat sekoittaa reaktiivista logiikkaa ei-reaktiiviseen logiikkaan.
Esimerkiksi, kuvittele, että haluat näyttää ilmoituksen kun käyttäjä yhdistää chattiin. Luet nykyisen teeman (tumma tai vaalea) propsista jotta voit näyttää ilmoituksen oikeassa värissä:
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
// ...
Kuitenkin, theme
on reaktiivinen arvo (se voi muuttua renderöinnin yhteydessä), ja kaikki Efektin lukemat reaktiiviset arvot täytyy lisätä sen riippuvuuksiin. Nyt sinun täytyy lisätä theme
Efektisi riippuvuuksiin:
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
return () => {
connection.disconnect()
};
}, [roomId, theme]); // ✅ Kaikki riippuvuudet määritelty
// ...
Kokeile tätä esimerkkiä ja katso jos huomaat ongelman tämän käyttökokemuksen kanssa:
import { useState, useEffect } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId, theme }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { showNotification('Connected!', theme); }); connection.connect(); return () => connection.disconnect(); }, [roomId, theme]); return <h1>Welcome to the {roomId} room!</h1> } export default function App() { const [roomId, setRoomId] = useState('general'); const [isDark, setIsDark] = useState(false); return ( <> <label> Choose the chat room:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">general</option> <option value="travel">travel</option> <option value="music">music</option> </select> </label> <label> <input type="checkbox" checked={isDark} onChange={e => setIsDark(e.target.checked)} /> Use dark theme </label> <hr /> <ChatRoom roomId={roomId} theme={isDark ? 'dark' : 'light'} /> </> ); }
Kun roomId
muuttuu, chat yhdistää uudelleen kuten odotit. Mutta koska theme
on myös riippuvuus, chat yhdistää myös joka kerta kun vaihdat tumman ja vaalean teeman välillä. Ei hyvä!
Toisin sanoen, et halua tämän rivin olevan reaktiivinen, vaikka se on Efektissä (joka on reaktiivinen):
// ...
showNotification('Connected!', theme);
// ...
Tarvitset tavan erottaa tämän ei-reaktiivisen logiikan reaktiivisesta Efektistä.
Efektitapahtuman määrittäminen
Käytä erityistä Hookia nimeltä useEffectEvent
irroittaaksesi tämän ei-reaktiivisen logiikan Efektistä:
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
// ...
Tässä, onConnected
kutsutaan Efektitapahtumaksi. Se on osa Efektisi logiikkaa, mutta se käyttäytyy enemmän tapahtumankäsittelijän tavoin. Logiikka sen sisällä ei ole reaktiivista, ja se “näkee” aina viimeisimmät propsin ja tilan arvot.
Nyt voit kutsua onConnected
Efektitapahtumaa Efektisi sisältä:
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ Kaikki riippuvuudet määritelty
// ...
Tämä korjaa ongelman. Huomaa, että sinun täytyi poistaa onConnected
Efektisi riippuvuuksien listalta. Efektitapahtumat eivät ole reaktiivisia ja ne täytyy jättää pois riippuvuuksien listalta.
Varmista, että uusi käyttäytyminen toimii odotetusti:
import { useState, useEffect } from 'react'; import { experimental_useEffectEvent as useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId, theme }) { const onConnected = useEffectEvent(() => { showNotification('Connected!', theme); }); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { onConnected(); }); connection.connect(); return () => connection.disconnect(); }, [roomId]); return <h1>Welcome to the {roomId} room!</h1> } export default function App() { const [roomId, setRoomId] = useState('general'); const [isDark, setIsDark] = useState(false); return ( <> <label> Choose the chat room:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">general</option> <option value="travel">travel</option> <option value="music">music</option> </select> </label> <label> <input type="checkbox" checked={isDark} onChange={e => setIsDark(e.target.checked)} /> Use dark theme </label> <hr /> <ChatRoom roomId={roomId} theme={isDark ? 'dark' : 'light'} /> </> ); }
Voit ajatella Efektitapahtumien olevat hyvin samanlaisia kuin tapahtumankäsittelijät. Pääero on, että tapahtumankäsittelijät suoritetaan vastauksena käyttäjän vuorovaikutukseen, kun taas Efektitapahtumat käynnistetään Efektistäsi. Efektitapahtumat antavat sinun “katkaista ketjun” Efektien reaktiivisuuden ja koodin välillä, jonka ei tulisi olla reaktiivista.
Viimeisimmän propsin ja tilan lukeminen Efektitapahtumilla
Efektitapahtumien avulla voit korjata monia tapauksia, joissa saattaisit kokea houkutuksen linterin hiljentämiseen.
Sanotaan esimerkiksi, että sinulla on Efekti, joka kerää sivun vierailut:
function Page() {
useEffect(() => {
logVisit();
}, []);
// ...
}
Myöhemmin, lisäät useita reittejä sivustollesi. Nyt Page
komponenttisi saa url
propsin nykyisellä polulla. Haluat välittää url
:n osana logVisit
kutsua, mutta riippuvuus-linteri valittaa:
function Page({ url }) {
useEffect(() => {
logVisit(url);
}, []); // 🔴 React Hook useEffect has a missing dependency: 'url'
// ...
}
Ajattele mitä haluat koodin tekevän. Nyt haluat sen kirjaavan lokin jokaisesta eri URL:n vierailusta, koska jokainen URL edustaa eri sivua. Toisin sanoen, tämä logVisit
kutsu täytyy olla reaktiivinen url
:n suhteen. Tämän takia, tässä tapauksessa, on järkevää seurata riippuvuus-linteriä, ja lisätä url
riippuvuudeksi:
function Page({ url }) {
useEffect(() => {
logVisit(url);
}, [url]); // ✅ Kaikki riippuvuudet määritelty
// ...
}
Nyt sanotaan, että haluat sisällyttää ostoskorin tuotteiden määrän jokaisen sivun vierailun yhteydessä:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
useEffect(() => {
logVisit(url, numberOfItems);
}, [url]); // 🔴 React Hook useEffect has a missing dependency: 'numberOfItems'
// ...
}
Käytit numberOfItems
Efektin sisällä, joten linter pyytää lisäämään sen riippuvuudeksi. Et kuitenkaan halua logVisit
kutsun olevan reaktiivinen numberOfItems
:n suhteen. Jos käyttäjä laittaa jotain ostoskoriin, ja numberOfItems
muuttuu, tämä ei tarkoita että käyttäjä vieraili sivulla uudelleen. Toisin sanoen, sivulla vierailu on, jollain tapaa, “tapahtuma”. Se tapahtuu tiettynä hetkenä.
Jaa koodi kahteen osaan:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
const onVisit = useEffectEvent(visitedUrl => {
logVisit(visitedUrl, numberOfItems);
});
useEffect(() => {
onVisit(url);
}, [url]); // ✅ Kaikki riippuvuudet määritelty
// ...
}
Tässä, onVisit
on Efektitapahtuma. Koodi sen sisällä ei ole reaktiivista. Tämän takia voit käyttää numberOfItems
:a (tai mitä tahansa reaktiivista arvoa!) huoletta, että se aiheuttaisi ympäröivän koodin uudelleen suorittamisen muutoksien yhteydessä.
Toisaalta, Efekti itsessään pysyy reaktiivisena. Koodi Efektin sisällä käyttää url
propsia, joten Efekti tullaan suoritamaan uudelleen jokaisen uudelleenrenderöinnin yhteydessä eri url
:lla. Tämä, puolestaan, kutsuu onVisit
Efektitapahtumaa.
Tämän seurauksena kutsut logVisit
funktiota jokaisen url
muutoksen yhteydessä, ja luet aina uusimman numberOfItems
:n. Kuitenkin, jos numberOfItems
muuttuu itsestään, tämä ei aiheuta koodin uudelleen suorittamista.
Syväsukellus
Olemassa olevissa koodipohjissa, saatat törmätä linterin hiljentämiseen tällä tavalla:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
useEffect(() => {
logVisit(url, numberOfItems);
// 🔴 Vältä linterin hiljentämistä tällä tavalla:
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url]);
// ...
}
Kun useEffectEvent
:sta tulee vakaa osa Reactia, suosittelemme älä koskaan hiljennä linteriä.
Ensimmäinen haittapuoli linterin hiljentämisessä on, että React ei enää varoita sinua kun Efektisi tarvitsee “reagoida” uuteen reaktiiviseen riippuvuuteen, jonka olet lisännyt koodiisi. Aiemmassa esimerkissä, lisäsit url
:n riippuvuudeksi koska React muistutti sinua siitä. Et enää saa tällaisia muistutuksia tulevista muutoksista Efektiin jos hiljennät linterin. Tämä johtaa bugeihin.
Tässä on esimerkki sekavasta viasta linterin hiljentäminen on aiheuttanut. Tässä esimerkissä, handleMove
funktion on tarkoitus lukea nykyinen canMove
tilamuuttujan arvo päättääkseen seuraako piste hiirtä. Kuitenkin, canMove
on aina true
handleMove
:n sisällä.
Löydätkö miksi?
import { useState, useEffect } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); const [canMove, setCanMove] = useState(true); function handleMove(e) { if (canMove) { setPosition({ x: e.clientX, y: e.clientY }); } } useEffect(() => { window.addEventListener('pointermove', handleMove); return () => window.removeEventListener('pointermove', handleMove); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> <label> <input type="checkbox" checked={canMove} onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> <hr /> <div style={{ position: 'absolute', backgroundColor: 'pink', borderRadius: '50%', opacity: 0.6, transform: `translate(${position.x}px, ${position.y}px)`, pointerEvents: 'none', left: -20, top: -20, width: 40, height: 40, }} /> </> ); }
Ongelma koodissa on riippuvuuslinterin hiljentäminen. Jos poistat hiljennyksen, huomaat että tämä Efekti tarvitsee handleMove
funktion riippuvuudeksi. Tämä on järkevää: handleMove
on määritelty komponentin sisällä, joka tekee siitä reaktiivisen arvon. Jokaisen reaktiivisen arvon täytyy olla määritelty riippuvuudeksi, tai se voi vanhentua ajan myötä!
Alkuperäisen koodin kirjoittaja on “valehdellut” Reactille kertomalla sille, että Efekti ei riipu ([]
) yhdestäkään reaktiivisesta arvosta. Tämän takia React ei uudelleen synkronoi Efektia sen jälkeen kun canMove
on muuttunut (ja handleMove
:a sen mukana). Koska React ei uudelleen synkronoinut Efektiä, handleMove
joka on liitetty tapahtumankäsittelijäksi on handleMove
funktio, joka on luotu ensimmäisen renderöinnin aikana. Ensimmäisen renderöinnin aikana, canMove
oli true
, minkä takia handleMove
ensimmäiseltä renderöinniltä näkee aina tämän arvon.
Jos et koskaan hiljennä linteria, et koskaan näe ongelmia vanhentuneiden arvojen kanssa.
Käyttämällä useEffectEvent
hookkia, ei ole tarpeen “valehdella” linterille, ja koodi toimii kuten oletat:
import { useState, useEffect } from 'react'; import { experimental_useEffectEvent as useEffectEvent } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); const [canMove, setCanMove] = useState(true); const onMove = useEffectEvent(e => { if (canMove) { setPosition({ x: e.clientX, y: e.clientY }); } }); useEffect(() => { window.addEventListener('pointermove', onMove); return () => window.removeEventListener('pointermove', onMove); }, []); return ( <> <label> <input type="checkbox" checked={canMove} onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> <hr /> <div style={{ position: 'absolute', backgroundColor: 'pink', borderRadius: '50%', opacity: 0.6, transform: `translate(${position.x}px, ${position.y}px)`, pointerEvents: 'none', left: -20, top: -20, width: 40, height: 40, }} /> </> ); }
Tämä ei tarkoita, että useEffectEvent
olisi aina oikea ratkaisu. Sinun tulisi käyttää sitä vain koodiriveillä, joiden et halua olevan reaktiivisia. Yllä olevassa hiekkalaatikossa, et halunnut Efektin koodin olevan reaktiivista canMove
:n suhteen. Tämän takia oli järkevää irroittaa Efektitapahtuma.
Lue Riippuvuuksien poistaminen Efektista muita oikeita vaihtoehtoja linterin hiljentämisen sijaan.
Efektitapahtumien rajoitteet
Efektitapahtumat ovat hyvin rajattuja siinä miten voit käyttää niitä:
- Kutsu vain Efektin sisältä.
- Älä koskaan välitä niitä toisiin komponentteihin tai Hookkeihin.
Esimerkiksi, älä määrittele ja välitä Efektitapahtumaa näin:
function Timer() {
const [count, setCount] = useState(0);
const onTick = useEffectEvent(() => {
setCount(count + 1);
});
useTimer(onTick, 1000); // 🔴 Vältä: Efektitapahtuman välittäminen
return <h1>{count}</h1>
}
function useTimer(callback, delay) {
useEffect(() => {
const id = setInterval(() => {
callback();
}, delay);
return () => {
clearInterval(id);
};
}, [delay, callback]); // Täytyy määritellä "callback" riippuvuuksissa
}
Sen sijaan, määrittele Efektitapahtumat aina suoraan Efektien vieressä, jotka niitä käyttävät:
function Timer() {
const [count, setCount] = useState(0);
useTimer(() => {
setCount(count + 1);
}, 1000);
return <h1>{count}</h1>
}
function useTimer(callback, delay) {
const onTick = useEffectEvent(() => {
callback();
});
useEffect(() => {
const id = setInterval(() => {
onTick(); // ✅ Hyvä: Kutsutaan vain paikallisesti Efektin sisässä
}, delay);
return () => {
clearInterval(id);
};
}, [delay]); // Ei tarvetta määritellä "onTick" (Efektitapahtumaa) riippuvuudeksi
}
Efektitapahtumat ovat ei-reaktiivisia “palasia” Efektisi koodistasi. Niiden tulisi olla Efektin vieressä, joka niitä käyttää.
Kertaus
- Tapahtumankäsittelijät suoritetaan vastauksena tiettyihin vuorovaikutuksiin.
- Efektit suoritetaan aina kun synkronointi on tarpeen.
- Logiikka tapahtumankäsittelijän sisällä ei ole reaktiivista.
- Logiikka Efektissa on reaktiivista.
- Voit siirtää ei-reaktiivisen logiikan Efektista Efektitapahtumiin.
- Kutsu vain Efektitapahtumia Efektien sisältä.
- Älä välitä Efektitapahtumia muihin komponentteihin tai Hookkeihin.
Haaste 1 / 4: Korjaa muuttuja joka ei päivity
Tämä Timer
komponentti pitää count
tilamuuttujan, joka kasvaa joka sekunti. Arvo jolla se kasvaa on tallennettu increment
tilamuuttujaan. Voit hallita increment
muuttujaa plus ja miinus napeilla.
Kuitenkin, sillä ei ole väliä miten monta kertaa painat plus painiketta, laskuri kasvaa silti yhdellä joka sekunti. Mikä on vikana tässä koodissa? Miksi increment
on aina yhtä suuri kuin 1
Efektin koodissa? Etsi virhe ja korjaa se.
import { useState, useEffect } from 'react'; export default function Timer() { const [count, setCount] = useState(0); const [increment, setIncrement] = useState(1); useEffect(() => { const id = setInterval(() => { setCount(c => c + increment); }, 1000); return () => { clearInterval(id); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> <h1> Counter: {count} <button onClick={() => setCount(0)}>Reset</button> </h1> <hr /> <p> Every second, increment by: <button disabled={increment === 0} onClick={() => { setIncrement(i => i - 1); }}>–</button> <b>{increment}</b> <button onClick={() => { setIncrement(i => i + 1); }}>+</button> </p> </> ); }