From 46cc557473e7849f11bd33cc744bf798aae4ec13 Mon Sep 17 00:00:00 2001 From: Miro Rauhala <4082806+mirorauhala@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:54:02 +0300 Subject: [PATCH] Translate Removing Effect Dependencies --- .../learn/removing-effect-dependencies.md | 408 +++++++++--------- 1 file changed, 204 insertions(+), 204 deletions(-) diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index 0a5151daa..f370e7032 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -1,26 +1,26 @@ --- -title: 'Removing Effect Dependencies' +title: 'Efektin riippuvuuksien poistaminen' --- -When you write an Effect, the linter will verify that you've included every reactive value (like props and state) that the Effect reads in the list of your Effect's dependencies. This ensures that your Effect remains synchronized with the latest props and state of your component. Unnecessary dependencies may cause your Effect to run too often, or even create an infinite loop. Follow this guide to review and remove unnecessary dependencies from your Effects. +Kun kirjoitat Efektia, linter tarkistaa, että olet sisällyttänyt jokaisen reaktiivisen arvon (kuten propsit ja tilan) Efektisi riippuvuuslistalle. Tämä varmistaa, että Efektisi pysyy synkronoituna komponenttisi viimeisimpien propsien ja tilan kanssa. Tarpeettomat riippuvuudet voivat aiheuttaa Efektisi suorittamisen liian usein tai jopa luoda äärettömän silmukan. Seuraa tätä opasta tarkistaaksesi ja poistaaksesi tarpeettomat riippuvuudet Efekteistäsi. -- How to fix infinite Effect dependency loops -- What to do when you want to remove a dependency -- How to read a value from your Effect without "reacting" to it -- How and why to avoid object and function dependencies -- Why suppressing the dependency linter is dangerous, and what to do instead +- Miten korjata loputtomat Efekti-riippuvuus-silmukat +- Mitä tehdä kun haluat poistaa riippuvuuden +- Miten lukea arvo Efektistasi "reagoimatta" siihe +- Miten ja miksi välttää olioiden ja funktioiden riippuvuuksia +- Miksi riippuvuus-linterin hiljentäminen on vaarallista ja mitä tehdä sen sijaan -## Dependencies should match the code {/*dependencies-should-match-the-code*/} +## Riippuvuuksien tulisi vastata koodia {/*dependencies-should-match-the-code*/} -When you write an Effect, you first specify how to [start and stop](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) whatever you want your Effect to be doing: +Kun kirjoitat Efektia, sinun täytyy ensin määritellä miten [aloittaa ja lopettaa](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) mitä ikinä haluat Efektisi tekevän: ```js {5-7} const serverUrl = 'https://localhost:1234'; @@ -34,7 +34,7 @@ function ChatRoom({ roomId }) { } ``` -Then, if you leave the Effect dependencies empty (`[]`), the linter will suggest the correct dependencies: +Sitten, jos jätät Efektisi riippuvuudet tyhjäksi (`[]`), linter suosittelee oikeita riippuvuuksia: @@ -77,7 +77,7 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti return { connect() { console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); @@ -96,7 +96,7 @@ button { margin-left: 10px; } -Fill them in according to what the linter says: +Täytä ne sen mukaan mitä linter kertoo: ```js {6} function ChatRoom({ roomId }) { @@ -104,12 +104,12 @@ function ChatRoom({ roomId }) { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Kaikki riippuvuudet määritelty // ... } ``` -[Effects "react" to reactive values.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) Since `roomId` is a reactive value (it can change due to a re-render), the linter verifies that you've specified it as a dependency. If `roomId` receives a different value, React will re-synchronize your Effect. This ensures that the chat stays connected to the selected room and "reacts" to the dropdown: +[Efektit "reagoivat" reaktiivisiin arvoihin.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) Koska `roomId` on reaktiivinen arvo (se voi muuttua renderöinnin seurauksena), linter tarkistaa, että olet määritellyt sen riippuvuutena. Jos `roomId` vastaanottaa eri arvon, React synkronoi Efektisi uudelleen. Tämä takaa, että chat pysyy yhdistettynä valittuun huoneeseen ja "reagoi" pudotusvalikkoon: @@ -152,7 +152,7 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti return { connect() { console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); @@ -171,24 +171,24 @@ button { margin-left: 10px; } -### To remove a dependency, prove that it's not a dependency {/*to-remove-a-dependency-prove-that-its-not-a-dependency*/} +### Poistaaksesi riippuvuuden, todista ettei se ole riippuvuus {/*to-remove-a-dependency-prove-that-its-not-a-dependency*/} -Notice that you can't "choose" the dependencies of your Effect. Every reactive value used by your Effect's code must be declared in your dependency list. The dependency list is determined by the surrounding code: +Huomaa, ettet voi "päättää" Efektisi riippuvuuksia. Jokainen reaktiivinen arvo, jota Efektisi koodi käyttää, täytyy olla määritelty riippuvuuslistalla. Riippuvuuslista määräytyy ympäröivän koodin mukaan: ```js [[2, 3, "roomId"], [2, 5, "roomId"], [2, 8, "roomId"]] const serverUrl = 'https://localhost:1234'; -function ChatRoom({ roomId }) { // This is a reactive value +function ChatRoom({ roomId }) { // Tämä on reaktiivinen arvo useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads that reactive value + const connection = createConnection(serverUrl, roomId); // Tämä Efekti lukee reaktiivisen arvon connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ So you must specify that reactive value as a dependency of your Effect + }, [roomId]); // ✅ Joten sinun täytyy määritellä tämä reaktiivinen arvo Efektisi riippuvuudeksi // ... } ``` -[Reactive values](/learn/lifecycle-of-reactive-effects#all-variables-declared-in-the-component-body-are-reactive) include props and all variables and functions declared directly inside of your component. Since `roomId` is a reactive value, you can't remove it from the dependency list. The linter wouldn't allow it: +[Reaktiiviset arvot](/learn/lifecycle-of-reactive-effects#all-variables-declared-in-the-component-body-are-reactive) ovat propsit ja kaikki muuttujat ja funktiot määritelty suoraan komponentin sisällä. Koska `roomId` on reaktiivinen arvo, et voi poistaa sitä riippuvuuslistalta. Linter ei sallisi sitä: ```js {8} const serverUrl = 'https://localhost:1234'; @@ -203,20 +203,20 @@ function ChatRoom({ roomId }) { } ``` -And the linter would be right! Since `roomId` may change over time, this would introduce a bug in your code. +Ja linter on oikeassa! Koska `roomId` voi muuttua ajan myötä, tämä aiheuttaisi bugin koodiisi. -**To remove a dependency, "prove" to the linter that it *doesn't need* to be a dependency.** For example, you can move `roomId` out of your component to prove that it's not reactive and won't change on re-renders: +**Poistaaksesi riippuvuuden, "todista" linterille, että sen *ei tarvitse* olla riippuvuus.** Voit esimerkiksi siirtää `roomId` komponentin ulkopuolelle todistaaksesi, ettei se ole reaktiivinen eikä muutu uudelleenrenderöinneissä: ```js {2,9} const serverUrl = 'https://localhost:1234'; -const roomId = 'music'; // Not a reactive value anymore +const roomId = 'music'; // Ei ole reaktiivinen arvo enää function ChatRoom() { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Kaikki riippuvuudet määritelty // ... } ``` @@ -244,7 +244,7 @@ export default function ChatRoom() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti return { connect() { console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); @@ -263,43 +263,43 @@ button { margin-left: 10px; } -This is why you could now specify an [empty (`[]`) dependency list.](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) Your Effect *really doesn't* depend on any reactive value anymore, so it *really doesn't* need to re-run when any of the component's props or state change. +Tämän takia voit nyt määritellä [tyhjän (`[]`) riippuvuuslistan.](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) Efektisi *ei todellakaan* riipu enää yhdestäkään reaktiivisesta arvosta, joten sitä *ei todellakaan* tarvitse suorittaa uudelleen kun komponentin propsit tai tila muuttuvat. -### To change the dependencies, change the code {/*to-change-the-dependencies-change-the-code*/} +### Muuttaaksesi riippuvuuksia, muuta koodia {/*to-change-the-dependencies-change-the-code*/} -You might have noticed a pattern in your workflow: +Olet saattanut huomata kaavan työskentelyssäsi: -1. First, you **change the code** of your Effect or how your reactive values are declared. -2. Then, you follow the linter and adjust the dependencies to **match the code you have changed.** -3. If you're not happy with the list of dependencies, you **go back to the first step** (and change the code again). +1. Ensiksi, **muutat Efektisi koodia** tai miten reaktiiviset arvot on määritelty. +2. Sitten, seuraa linteria ja muuta riippuvuudet **vastaamaan muuttunutta koodia.** +3. Jos et ole tyytyväinen riippuvuuslistaan, voit **palata ensimmäiseen vaiheeseen** (ja muuttaa koodia uudelleen). -The last part is important. **If you want to change the dependencies, change the surrounding code first.** You can think of the dependency list as [a list of all the reactive values used by your Effect's code.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) You don't *choose* what to put on that list. The list *describes* your code. To change the dependency list, change the code. +Viimeinen kohta on tärkeä. **Jos haluat muuttaa riippuvuuksia, muuta ensin ympäröivää koodia.** Voit ajatella riippuvuuslistaa [listana kaikista Efektisi koodissa käytetyistä reaktiivisista arvoista.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Et *valitse* mitä listaan laitat. Lista *kuvastaa* koodiasi. Muuttaaksesi riippuvuuslistaa, muuta koodia. -This might feel like solving an equation. You might start with a goal (for example, to remove a dependency), and you need to "find" the code matching that goal. Not everyone finds solving equations fun, and the same thing could be said about writing Effects! Luckily, there is a list of common recipes that you can try below. +Tämä saattaa tuntua yhtälön ratkaisemiselta. Saatat aloittaa tavoitteesta (esimerkiksi poistaa riippuvuus), ja sinun täytyy "löytää" koodi, joka vastaa tavoitetta. Kaikki eivät pidä yhtälöiden ratkaisemisesta, ja samaa voisi sanoa Efektien kirjoittamisesta! Onneksi alla on lista yleisistä resepteistä, joita voit kokeilla. -If you have an existing codebase, you might have some Effects that suppress the linter like this: +Jos sinulla on olemassa oleva koodipohja, sinulla saattaa olla joitain Efektejä, jotka hiljentävät linterin näin: ```js {3-4} useEffect(() => { // ... - // 🔴 Avoid suppressing the linter like this: + // 🔴 Vältä linterin hiljentämistä tällä tavalla: // eslint-ignore-next-line react-hooks/exhaustive-deps }, []); ``` -**When dependencies don't match the code, there is a very high risk of introducing bugs.** By suppressing the linter, you "lie" to React about the values your Effect depends on. +**Kun riippuvuudet eivät vastaa koodia, on erittäin suuri riski, että aiheutat bugeja.** Hiljentämällä linterin, "valehtelet" Reactille Efektisi riippuvuuksista. -Instead, use the techniques below. +Sen sijaan, käytä alla olevia tekniikoita. -#### Why is suppressing the dependency linter so dangerous? {/*why-is-suppressing-the-dependency-linter-so-dangerous*/} +#### Miksi linterin hiljentäminen on niin vaarallista? {/*why-is-suppressing-the-dependency-linter-so-dangerous*/} -Suppressing the linter leads to very unintuitive bugs that are hard to find and fix. Here's one example: +Linterin hiljentäminen johtaa erittäin epäintuitiivisiin bugeihin, jotka ovat vaikeita löytää ja korjata. Tässä on yksi esimerkki: @@ -348,31 +348,31 @@ button { margin: 10px; } -Let's say that you wanted to run the Effect "only on mount". You've read that [empty (`[]`) dependencies](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) do that, so you've decided to ignore the linter, and forcefully specified `[]` as the dependencies. +Sanotaan, että haluat suorittaa Efektin "vain mountissa". Olet lukenut, että [tyhjät (`[]`) riippuvuudet](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) tekevät sen, joten olet päättänyt hiljentää linterin ja määritellä `[]` riippuvuudeksi. -This counter was supposed to increment every second by the amount configurable with the two buttons. However, since you "lied" to React that this Effect doesn't depend on anything, React forever keeps using the `onTick` function from the initial render. [During that render,](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time) `count` was `0` and `increment` was `1`. This is why `onTick` from that render always calls `setCount(0 + 1)` every second, and you always see `1`. Bugs like this are harder to fix when they're spread across multiple components. +Tämän laskurin oli tarkoitus kasvaa joka sekunti kahdella painikkeella määriteltävällä määrällä. Kuitenkin, koska "valehtelit" Reactille, että tämä Efekti ei riipu mistään, React käyttää ikuisesti `onTick` funktiota ensimmäisestä renderöinnistä. [Tuon renderöinnin aikana,](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time) `count` oli `0` ja `increment` oli `1`. Tämän takia `onTick` tuosta renderöinnistä kutsuu aina `setCount(0 + 1)` joka sekunti, ja näet aina `1`. Tällaisia bugeja on vaikeampi korjata kun ne ovat levinneet useisiin komponentteihin. -There's always a better solution than ignoring the linter! To fix this code, you need to add `onTick` to the dependency list. (To ensure the interval is only setup once, [make `onTick` an Effect Event.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)) +On aina parempi vaihtoehto kuin linterin hiljentäminen! Korjataksesi tämän koodin, lisää `onTick` riippuvuuslistalle. (Varmistaaksesi, että laskuri asetetaan vain kerran, [tee `onTick`:sta Efektitapahtuma.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)) -**We recommend treating the dependency lint error as a compilation error. If you don't suppress it, you will never see bugs like this.** The rest of this page documents the alternatives for this and other cases. +**Suosittelemme kohtelemaan riippuvuus-linterin virhettä käännösvirheenä. Jos et hiljennä sitä, et koskaan näe tällaisia bugeja.** Loput tästä sivusta listaavat vaihtoehtoja tähän ja muihin tapauksiin. -## Removing unnecessary dependencies {/*removing-unnecessary-dependencies*/} +## Turhien riippuvuuksien poistaminen {/*removing-unnecessary-dependencies*/} -Every time you adjust the Effect's dependencies to reflect the code, look at the dependency list. Does it make sense for the Effect to re-run when any of these dependencies change? Sometimes, the answer is "no": +Joka kerta kun muutat Efektisi riippuvuuksia koodin mukaan, katso riippuvuuslistaa. Onko järkevää, että Efekti suoritetaan uudelleen kun jokin näistä riippuvuuksista muuttuu? Joskus vastaus on "ei": -* You might want to re-execute *different parts* of your Effect under different conditions. -* You might want to only read the *latest value* of some dependency instead of "reacting" to its changes. -* A dependency may change too often *unintentionally* because it's an object or a function. +* Saatat haluta suorittaa *eri osia* Efektistä eri olosuhteissa. +* Saatat haluta lukea *viimeisimmän arvon* jostain riippuvuudesta sen sijaan, että "reagoisit" sen muutoksiin. +* Riippuvuus voi muuttua liian usein *vahingossa* koska se on olio tai funktio. -To find the right solution, you'll need to answer a few questions about your Effect. Let's walk through them. +Löytääksesi oikean ratkaisun, sinun täytyy vastata muutamaan kysymykseen Efektistäsi. Käydään ne läpi. -### Should this code move to an event handler? {/*should-this-code-move-to-an-event-handler*/} +### Pitäisikö tämä koodi siirtää tapahtumankäsittelijään? {/*should-this-code-move-to-an-event-handler*/} -The first thing you should think about is whether this code should be an Effect at all. +Ensimmäinen asia, jota sinun tulisi ajatella on pitäisikö tämä koodi olla Efekti ollenkaan. -Imagine a form. On submit, you set the `submitted` state variable to `true`. You need to send a POST request and show a notification. You've put this logic inside an Effect that "reacts" to `submitted` being `true`: +Kuvittele lomake. Kun lähetät sen, asetat `submitted` tilamuuttujan arvoksi `true`. Sinun täytyy lähettää POST-pyyntö ja näyttää ilmoitus. Olet laittanut tämän logiikan Efektiin, joka "reagoi" `submitted` arvon ollessa `true`: ```js {6-8} function Form() { @@ -380,7 +380,7 @@ function Form() { useEffect(() => { if (submitted) { - // 🔴 Avoid: Event-specific logic inside an Effect + // 🔴 Vältä: Tapahtumakohtainen logiikka Efektissa post('/api/register'); showNotification('Successfully registered!'); } @@ -394,7 +394,7 @@ function Form() { } ``` -Later, you want to style the notification message according to the current theme, so you read the current theme. Since `theme` is declared in the component body, it is a reactive value, so you add it as a dependency: +Myöhemmin, haluat tyylittää ilmoituksen viestin nykyisen teeman mukaan, joten luet nykyisen teeman. Koska `theme` on määritelty komponentin sisällä, se on reaktiivinen arvo, joten lisäät sen riippuvuudeksi: ```js {3,9,11} function Form() { @@ -403,45 +403,45 @@ function Form() { useEffect(() => { if (submitted) { - // 🔴 Avoid: Event-specific logic inside an Effect + // 🔴 Vältä: Tapahtumakohtainen logiikka Efektissa post('/api/register'); showNotification('Successfully registered!', theme); } - }, [submitted, theme]); // ✅ All dependencies declared + }, [submitted, theme]); // ✅ Kaikki riippuvuudet määritelty function handleSubmit() { setSubmitted(true); - } + } // ... } ``` -By doing this, you've introduced a bug. Imagine you submit the form first and then switch between Dark and Light themes. The `theme` will change, the Effect will re-run, and so it will display the same notification again! +Tekemällä tämän, olet aiheuttanut bugin. Kuvittele, että lähetät lomakkeen ensin ja sitten vaihdat teeman tummaksi. `theme` muuttuu, Efekti suoritetaan uudelleen, ja se näyttää saman ilmoituksen uudelleen! -**The problem here is that this shouldn't be an Effect in the first place.** You want to send this POST request and show the notification in response to *submitting the form,* which is a particular interaction. To run some code in response to particular interaction, put that logic directly into the corresponding event handler: +**Ongelma on, että sen ei pitäisi olla Efekti alunperinkään.** Haluat lähettää POST-pyynnön ja näyttää ilmoituksen vastauksena *lomakkeen lähettämisen,* joka on tietty interaktio. Suorittaaksesi koodia tiettyyn interaktioon, laita se suoraan vastaavaan tapahtumankäsittelijään: ```js {6-7} function Form() { const theme = useContext(ThemeContext); function handleSubmit() { - // ✅ Good: Event-specific logic is called from event handlers + // ✅ Hyvä: Tapahtumakohtainen logiikka kutsutaan tapahtumakäsittelijästä post('/api/register'); showNotification('Successfully registered!', theme); - } + } // ... } ``` -Now that the code is in an event handler, it's not reactive--so it will only run when the user submits the form. Read more about [choosing between event handlers and Effects](/learn/separating-events-from-effects#reactive-values-and-reactive-logic) and [how to delete unnecessary Effects.](/learn/you-might-not-need-an-effect) +Nyt kun koodi on tapahtumakäsittelijässä, se ei ole reaktiivista--joten se suoritetaan vain kun käyttäjä lähettää lomakkeen. Lue [valinta tapahtumankäsittelijän ja Efektin välillä](/learn/separating-events-from-effects#reactive-values-and-reactive-logic) ja [miten poistaa turhia Efekteja.](/learn/you-might-not-need-an-effect) -### Is your Effect doing several unrelated things? {/*is-your-effect-doing-several-unrelated-things*/} +### Tekeekö Efektisi useita toistaan riippumattomia asioita? {/*is-your-effect-doing-several-unrelated-things*/} -The next question you should ask yourself is whether your Effect is doing several unrelated things. +Seuraava kysymys jota sinun tulisi kysyä itseltäsi on tekeekö Efektisi useita toistaan riippumattomia asioita. -Imagine you're creating a shipping form where the user needs to choose their city and area. You fetch the list of `cities` from the server according to the selected `country` to show them in a dropdown: +Kuvittele että luot toimituslomakkeen, jossa käyttäjän täytyy valita kaupunki ja alue. Haet `cities` listan palvelimelta valitun `country`:n mukaan näyttääksesi ne pudotusvalikossa: ```js function ShippingForm({ country }) { @@ -460,14 +460,14 @@ function ShippingForm({ country }) { return () => { ignore = true; }; - }, [country]); // ✅ All dependencies declared + }, [country]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -This is a good example of [fetching data in an Effect.](/learn/you-might-not-need-an-effect#fetching-data) You are synchronizing the `cities` state with the network according to the `country` prop. You can't do this in an event handler because you need to fetch as soon as `ShippingForm` is displayed and whenever the `country` changes (no matter which interaction causes it). +Tämä on esimerkki [datan hakemisesta Efektissä.](/learn/you-might-not-need-an-effect#fetching-data) Synkronoit `cities` tilan verkon kanssa `country` propsin mukaan. Et voi tehdä tätä tapahtumankäsittelijässä koska sinun täytyy hakea heti kun `ShippingForm` näytetään ja aina kun `country` muuttuu (riippumatta siitä mikä interaktio aiheuttaa sen). -Now let's say you're adding a second select box for city areas, which should fetch the `areas` for the currently selected `city`. You might start by adding a second `fetch` call for the list of areas inside the same Effect: +Sanotaan, että lisäät toisen pudotusvalikon kaupunkien alueille, joka hakee `areas` valitun `city`:n mukaan. Saatat aloittaa lisäämällä toisen `fetch` kutsun alueiden listalle samassa Efektissä: ```js {15-24,28} function ShippingForm({ country }) { @@ -484,7 +484,7 @@ function ShippingForm({ country }) { setCities(json); } }); - // 🔴 Avoid: A single Effect synchronizes two independent processes + // 🔴 Vältä: Yksi Efekti synkronoi kahta erillistä prosessia if (city) { fetch(`/api/areas?city=${city}`) .then(response => response.json()) @@ -497,19 +497,19 @@ function ShippingForm({ country }) { return () => { ignore = true; }; - }, [country, city]); // ✅ All dependencies declared + }, [country, city]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -However, since the Effect now uses the `city` state variable, you've had to add `city` to the list of dependencies. That, in turn, introduced a problem: when the user selects a different city, the Effect will re-run and call `fetchCities(country)`. As a result, you will be unnecessarily refetching the list of cities many times. +Kuitenkin, koska Efekti käyttää nyt `city` tilamuuttujaa, olet joutunut lisäämään `city` riippuvuuslistalle. Tämä aiheutti ongelman: kun käyttäjä valitsee eri kaupungin, Efekti suoritetaan uudelleen ja kutsuu `fetchCities(country)`. Tämän takia haet tarpeettomasti kaupunkilistan monta kertaa. -**The problem with this code is that you're synchronizing two different unrelated things:** +**Ongelma tässä koodissa on, että synkronoit kaksi erillistä asiaa:** -1. You want to synchronize the `cities` state to the network based on the `country` prop. -1. You want to synchronize the `areas` state to the network based on the `city` state. +1. Haluat synkronoida `cities` tilan verkon kanssa `country` propsin mukaan. +1. Haluat synkronoida `areas` tilan verkon kanssa `city` tilan mukaan. -Split the logic into two Effects, each of which reacts to the prop that it needs to synchronize with: +Jaa logiikka kahteen Efektiin, joista kumpikin reagoi siihen propiin, jonka kanssa se tarvitsee synkronoida: ```js {19-33} function ShippingForm({ country }) { @@ -526,7 +526,7 @@ function ShippingForm({ country }) { return () => { ignore = true; }; - }, [country]); // ✅ All dependencies declared + }, [country]); // ✅ Kaikki riippuvuudet määritelty const [city, setCity] = useState(null); const [areas, setAreas] = useState(null); @@ -544,18 +544,18 @@ function ShippingForm({ country }) { ignore = true; }; } - }, [city]); // ✅ All dependencies declared + }, [city]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -Now the first Effect only re-runs if the `country` changes, while the second Effect re-runs when the `city` changes. You've separated them by purpose: two different things are synchronized by two separate Effects. Two separate Effects have two separate dependency lists, so they won't trigger each other unintentionally. +Nyt ensimmäinen Efekti suoritetaan vain jos `country` muuttuu, kun taas toinen Efekti suoritetaan kun `city` muuttuu. Olet erottanut ne tarkoituksen mukaan: kaksi erillistä asiaa synkronoidaan kahdella erillisellä Efektillä. Kahdella erillisellä Efektillä on kaksi erillistä riippuvuuslistaa, joten ne eivät käynnistä toisiaan vahingossa. -The final code is longer than the original, but splitting these Effects is still correct. [Each Effect should represent an independent synchronization process.](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) In this example, deleting one Effect doesn't break the other Effect's logic. This means they *synchronize different things,* and it's good to split them up. If you're concerned about duplication, you can improve this code by [extracting repetitive logic into a custom Hook.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) +Lopullinen koodi on pidempi kuin alkuperäinen, mutta näiden Efektien jakaminen on silti oikein. [Kunkin Efektin tulisi edustaa erillistä synkronointiprosessia.](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) Tässä esimerkissä, yhden Efektin poistaminen ei riko toisen Efektin logiikkaa. Tämä tarkoittaa, että ne *synkronoivat eri asioita,* ja on hyvä jakaa ne osiin. Jos olet huolissasi toistosta, voit parantaa tätä koodia [poistamalla toistuvan logiikan omaksi Hookiksi.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) -### Are you reading some state to calculate the next state? {/*are-you-reading-some-state-to-calculate-the-next-state*/} +### Luetko jotain tilaa laskeaksesi seuraavan tilan? {/*are-you-reading-some-state-to-calculate-the-next-state*/} -This Effect updates the `messages` state variable with a newly created array every time a new message arrives: +Tämä Efekit päivittää `messages` tilamuuttujan uudella taulukolla joka kerta kun uusi viesti saapuu: ```js {2,6-8} function ChatRoom({ roomId }) { @@ -569,7 +569,7 @@ function ChatRoom({ roomId }) { // ... ``` -It uses the `messages` variable to [create a new array](/learn/updating-arrays-in-state) starting with all the existing messages and adds the new message at the end. However, since `messages` is a reactive value read by an Effect, it must be a dependency: +Se käyttää `messages` muuttujaa [luodakseen uuden taulukon](/learn/updating-arrays-in-state) joka alkaa kaikilla olemassaolevilla viesteillä ja lisää uuden viestin loppuun. Koska `messages` on reaktiivinen arvo, jota Efekti lukee, sen täytyy olla riippuvuus: ```js {7,10} function ChatRoom({ roomId }) { @@ -581,15 +581,15 @@ function ChatRoom({ roomId }) { setMessages([...messages, receivedMessage]); }); return () => connection.disconnect(); - }, [roomId, messages]); // ✅ All dependencies declared + }, [roomId, messages]); // ✅ Kaikki muuttujat määritelty // ... ``` -And making `messages` a dependency introduces a problem. +Ja `messages` muuttujan lisääminen riippuvuudeksi aiheuttaa ongelman. -Every time you receive a message, `setMessages()` causes the component to re-render with a new `messages` array that includes the received message. However, since this Effect now depends on `messages`, this will *also* re-synchronize the Effect. So every new message will make the chat re-connect. The user would not like that! +Joka kerta kun vastaanotat viestin, `setMessages()` aiheuttaa komponentin uudelleen renderöinnin uudella `messages` taulukolla, joka sisältää vastaanotetun viestin. Kuitenkin, koska tämä Efekti riippuu nyt `messages` muuttujasta, tämä *synkronoi myös* Efektin uudelleen. Joten jokainen uusi viesti aiheuttaa chatin uudelleenyhdistämisen. Käyttäjä ei pitäisi siitä! -To fix the issue, don't read `messages` inside the Effect. Instead, pass an [updater function](/reference/react/useState#updating-state-based-on-the-previous-state) to `setMessages`: +Korjataksesi ongelman, älä lue `messages` tilaa Efektissä. Sen sijaan, välitä [päivittäjäfunktion](/reference/react/useState#updating-state-based-on-the-previous-state) `setMessages`:lle: ```js {7,10} function ChatRoom({ roomId }) { @@ -601,21 +601,21 @@ function ChatRoom({ roomId }) { setMessages(msgs => [...msgs, receivedMessage]); }); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -**Notice how your Effect does not read the `messages` variable at all now.** You only need to pass an updater function like `msgs => [...msgs, receivedMessage]`. React [puts your updater function in a queue](/learn/queueing-a-series-of-state-updates) and will provide the `msgs` argument to it during the next render. This is why the Effect itself doesn't need to depend on `messages` anymore. As a result of this fix, receiving a chat message will no longer make the chat re-connect. +**Huomaa miten Efektisi ei lue `messages` muuttujaa ollenkaan.** Sinun täytyy vain välittää päivittäjäfunktio kuten `msgs => [...msgs, receivedMessage]`. React [laittaa päivittäjäfunktion jonoon](/learn/queueing-a-series-of-state-updates) ja tarjoaa `msgs` argumentin sille seuraavassa renderöinnissä. Tämän takia Efektin ei tarvitse enää riippua `messages` muuttujasta. Tämän korjauksen takia, chatin viestin vastaanottaminen ei enää aiheuta chatin uudelleenyhdistämistä. -### Do you want to read a value without "reacting" to its changes? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/} +### Haluatko lukea arvon "reagoimatta" sen muutoksiin? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/} -This section describes an **experimental API that has not yet been released** in a stable version of React. +Tämä osio kuvailee **kokeellista API:a, joka ei ole vielä julkaistu** vakaassa Reactin versiossa. -Suppose that you want to play a sound when the user receives a new message unless `isMuted` is `true`: +Oletetaan, että haluat toistaa äänen kun käyttäjä vastaanottaa uuden viestin, ellei `isMuted` ole `true`: ```js {3,10-12} function ChatRoom({ roomId }) { @@ -634,7 +634,7 @@ function ChatRoom({ roomId }) { // ... ``` -Since your Effect now uses `isMuted` in its code, you have to add it to the dependencies: +Koska Efektisi käyttää nyt `isMuted` koodissaan, sinun täytyy lisätä se riippuvuuslistalle: ```js {10,15} function ChatRoom({ roomId }) { @@ -651,13 +651,13 @@ function ChatRoom({ roomId }) { } }); return () => connection.disconnect(); - }, [roomId, isMuted]); // ✅ All dependencies declared + }, [roomId, isMuted]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -The problem is that every time `isMuted` changes (for example, when the user presses the "Muted" toggle), the Effect will re-synchronize, and reconnect to the chat. This is not the desired user experience! (In this example, even disabling the linter would not work--if you do that, `isMuted` would get "stuck" with its old value.) +Ongelma on joka kerta kun `isMuted` muuttuu (esimerkiksi, kun käyttäjä painaa "Muted" kytkintä), Efekti synkronoituu uudelleen ja yhdistää uudelleen chattiin. Tämä ei ole haluttu käyttäjäkokemus! (Tässä esimerkissä, jopa linterin poistaminen ei toimisi--jos teet sen, `isMuted` jäisi "jumiin" vanhaan arvoonsa.) -To solve this problem, you need to extract the logic that shouldn't be reactive out of the Effect. You don't want this Effect to "react" to the changes in `isMuted`. [Move this non-reactive piece of logic into an Effect Event:](/learn/separating-events-from-effects#declaring-an-effect-event) +Ratkaistaksesi tämän ongelman, sinun täytyy erottaa logiikka, joka ei saisi olla reaktiivista Efektistä. Et halua tämän Efektin "reagoivan" `isMuted` muutoksiin. [Siirrä tämä ei-reaktiivinen logiikka Efektitapahtumaan:](/learn/separating-events-from-effects#declaring-an-effect-event) ```js {1,7-12,18,21} import { useState, useEffect, useEffectEvent } from 'react'; @@ -680,15 +680,15 @@ function ChatRoom({ roomId }) { onMessage(receivedMessage); }); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -Effect Events let you split an Effect into reactive parts (which should "react" to reactive values like `roomId` and their changes) and non-reactive parts (which only read their latest values, like `onMessage` reads `isMuted`). **Now that you read `isMuted` inside an Effect Event, it doesn't need to be a dependency of your Effect.** As a result, the chat won't re-connect when you toggle the "Muted" setting on and off, solving the original issue! +Efektitapahtumien avulla voit jakaa Efektit reaktiivisiin osiin (joiden tulisi "reagoida" reaktiivisiin arvoihin kuten `roomId` ja niiden muutoksiin) ja ei-reaktiivisiin osiin (jotka lukevat vain viimeisimmät arvot, kuten `onMessage` lukee `isMuted`). **Nyt kun luet `isMuted` tilan Efektitapahtumassa, sen ei tarvitse olla Efektisi riippuvuus.** Tämän takia chat ei yhdistä uudelleen kun kytket "Muted" asetuksen päälle ja pois, ratkaisten alkuperäisen ongelman! -#### Wrapping an event handler from the props {/*wrapping-an-event-handler-from-the-props*/} +#### Tapahtumankäsittelijän kääriminen propseista {/*wrapping-an-event-handler-from-the-props*/} -You might run into a similar problem when your component receives an event handler as a prop: +Saatat törmätä samanlaiseen ongelmaan kun komponenttisi vastaanottaa tapahtumankäsittelijän propsina: ```js {1,8,11} function ChatRoom({ roomId, onReceiveMessage }) { @@ -701,11 +701,11 @@ function ChatRoom({ roomId, onReceiveMessage }) { onReceiveMessage(receivedMessage); }); return () => connection.disconnect(); - }, [roomId, onReceiveMessage]); // ✅ All dependencies declared + }, [roomId, onReceiveMessage]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -Suppose that the parent component passes a *different* `onReceiveMessage` function on every render: +Oletetaan, että vanhempi komponentti lähettää *eri* `onReceiveMessage` funktion joka renderöinnillä: ```js {3-5} ``` -Since `onReceiveMessage` is a dependency, it would cause the Effect to re-synchronize after every parent re-render. This would make it re-connect to the chat. To solve this, wrap the call in an Effect Event: +Koska `onReceiveMessage` on riippuvuus, sen tulisi aiheuttaa Efektin uudelleensynkronointi jokaisen yläkomponentin renderöinnin yhteydessä. Tämä saisi sen yhdistämään uudelleen chattiin. Ratkaistaksesi tämän, kääri kutsu Efektitapahtumaan: ```js {4-6,12,15} function ChatRoom({ roomId, onReceiveMessage }) { @@ -733,17 +733,17 @@ function ChatRoom({ roomId, onReceiveMessage }) { onMessage(receivedMessage); }); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -Effect Events aren't reactive, so you don't need to specify them as dependencies. As a result, the chat will no longer re-connect even if the parent component passes a function that's different on every re-render. +Efektitapahtumat eivät ole reaktiivisia, joten sinun ei tarvitse määritellä niitä riippuvuuksiksi. Tämän takia chat ei yhdistä uudelleen vaikka yläkomponentti lähettäisi funktion joka on eri jokaisella renderöinnillä. -#### Separating reactive and non-reactive code {/*separating-reactive-and-non-reactive-code*/} +#### Reaktiivisen ja ei-reaktiivisen koodin erottaminen {/*separating-reactive-and-non-reactive-code*/} -In this example, you want to log a visit every time `roomId` changes. You want to include the current `notificationCount` with every log, but you *don't* want a change to `notificationCount` to trigger a log event. +Tässä esimerkissä, haluat kirjata vierailun joka kerta kun `roomId` muuttuu. Haluat sisällyttää nykyisen `notificationCount` jokaiseen lokiin, mutta *et* halua muutoksen `notificationCount` tilaan käynnistävän lokitapahtumaa. -The solution is again to split out the non-reactive code into an Effect Event: +Ratkaisu on jälleen jakaa ei-reaktiivinen koodi Efektitapahtumaan: ```js {2-4,7} function Chat({ roomId, notificationCount }) { @@ -753,16 +753,16 @@ function Chat({ roomId, notificationCount }) { useEffect(() => { onVisit(roomId); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Kaikki riippuvuudet määritelty // ... } ``` -You want your logic to be reactive with regards to `roomId`, so you read `roomId` inside of your Effect. However, you don't want a change to `notificationCount` to log an extra visit, so you read `notificationCount` inside of the Effect Event. [Learn more about reading the latest props and state from Effects using Effect Events.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) +Haluat logiikkasi olevan reaktiivista `roomId` suhteen, joten luet `roomId` Efektissä. Kuitenkin, et halua muutoksen `notificationCount` tilaan kirjaavan ylimääräistä vierailua, joten luet `notificationCount` Efektitapahtumassa. [Lue lisää viimeisimpien propsien ja tilan lukemisesta Efekteistä käyttäen Efektitapahtumia.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) -### Does some reactive value change unintentionally? {/*does-some-reactive-value-change-unintentionally*/} +### Muuttuuko jokin reaktiivinen arvo tarkoituksettomasti? {/*does-some-reactive-value-change-unintentionally*/} -Sometimes, you *do* want your Effect to "react" to a certain value, but that value changes more often than you'd like--and might not reflect any actual change from the user's perspective. For example, let's say that you create an `options` object in the body of your component, and then read that object from inside of your Effect: +Joskus *haluat* Efektisi "reagoivan" tiettyyn arvoon, mutta arvo muuttuu useammin kuin haluaisit--ja se ei välttämättä heijasta mitään todellista muutosta käyttäjän näkökulmasta. Esimerkiksi, sanotaan että luot `options` olion komponentin sisällä, ja luet sitten sen olion Efektistä: ```js {3-6,9} function ChatRoom({ roomId }) { @@ -778,7 +778,7 @@ function ChatRoom({ roomId }) { // ... ``` -This object is declared in the component body, so it's a [reactive value.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) When you read a reactive value like this inside an Effect, you declare it as a dependency. This ensures your Effect "reacts" to its changes: +Tämä olio on määritelty komponentin sisällä, joten se on [reaktiivinen arvo](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) Kun luet tämän kaltaisen reaktiivisen arvon Efektin sisällä, määrittelet sen riippuvuudeksi. Tämä varmistaa, että Efektisi "reagoi" sen muutoksiin: ```js {3,6} // ... @@ -786,11 +786,11 @@ This object is declared in the component body, so it's a [reactive value.](/lear const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [options]); // ✅ All dependencies declared + }, [options]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -It is important to declare it as a dependency! This ensures, for example, that if the `roomId` changes, your Effect will re-connect to the chat with the new `options`. However, there is also a problem with the code above. To see it, try typing into the input in the sandbox below, and watch what happens in the console: +On tärkeää määritellä se riippuvuudeksi! Tämä takaa, jos esimerkiksi `roomId` muuttuisi, Efektisi yhdistäisi uudelleen chattiin uusilla `options` arvoilla. Kuitenkin, ylläolevassa koodissa on myös ongelma. Nähdäksesi sen, kokeile kirjoittaa syöttölaatikkoon alla olevassa hiekkalaatikossa, ja katso mitä tapahtuu konsolissa: @@ -803,7 +803,7 @@ const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - // Temporarily disable the linter to demonstrate the problem + // Tilapäisesti poista linter käytöstä ongelman osoittamiseksi // eslint-disable-next-line react-hooks/exhaustive-deps const options = { serverUrl: serverUrl, @@ -848,7 +848,7 @@ export default function App() { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti return { connect() { console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); @@ -867,30 +867,30 @@ button { margin-left: 10px; } -In the sandbox above, the input only updates the `message` state variable. From the user's perspective, this should not affect the chat connection. However, every time you update the `message`, your component re-renders. When your component re-renders, the code inside of it runs again from scratch. +Yllä olevassa esimerkissä, syöttölaatikko päivittää vain `message` tilamuuttujaa. Käyttäjän näkökulmasta, tämä ei vaikuta chat-yhteyteen. Kuitenkin, joka kerta kun päivität `message` tilaa, komponenttisi renderöityy. Kun komponenttisi renderöityy, koodi sen sisällä ajetaan alusta asti. -A new `options` object is created from scratch on every re-render of the `ChatRoom` component. React sees that the `options` object is a *different object* from the `options` object created during the last render. This is why it re-synchronizes your Effect (which depends on `options`), and the chat re-connects as you type. +Uusi `options` olio luodaan alusta asti jokaisella `ChatRoom` komponentin uudelleenrenderöinnillä. React näkee, että `options` olio on *eri olio* kuin `options` olio, joka luotiin edellisellä renderöinnillä. Tämän takia se synkronoi uudelleen Efektisi (joka riippuu `options` arvosta), ja chat yhdistää uudelleen kun kirjoitat. -**This problem only affects objects and functions. In JavaScript, each newly created object and function is considered distinct from all the others. It doesn't matter that the contents inside of them may be the same!** +**Tämä ongelma vaikuttaa vain olioihin ja funktioihin. JavaScriptissä, jokainen uusi luotu olio ja funktio katsotaan erilaiseksi kuin kaikki muut. Ei ole väliä, että niiden sisältö voi olla sama!** ```js {7-8} -// During the first render +// Ensimmäisen renderöinnin aikana const options1 = { serverUrl: 'https://localhost:1234', roomId: 'music' }; -// During the next render +// Seuraavan renderöinnin aikana const options2 = { serverUrl: 'https://localhost:1234', roomId: 'music' }; -// These are two different objects! +// Nämä ovat kaksi eri oliota! console.log(Object.is(options1, options2)); // false ``` -**Object and function dependencies can make your Effect re-synchronize more often than you need.** +**Olion ja funktion riippuvuudet voivat saada Efektisi synkronoimaan useammin kuin tarvitset.** -This is why, whenever possible, you should try to avoid objects and functions as your Effect's dependencies. Instead, try moving them outside the component, inside the Effect, or extracting primitive values out of them. +Tämän takia aina kun mahdollista, sinun tulisi pyrkiä välttämään olioita ja funktiota Efektin riippuvuuksina. Sen sijaan, kokeile siirtää niitä ulos komponentista, sisälle Efektiin, tai saada primitiiviset arvot niistä. -#### Move static objects and functions outside your component {/*move-static-objects-and-functions-outside-your-component*/} +#### Siirrä staattiset oliot ja funktiot komponentin ulkopuolelle {/*move-static-objects-and-functions-outside-your-component*/} -If the object does not depend on any props and state, you can move that object outside your component: +Jos olio ei riipu mistään propseista tai tilasta, voit siirtää sen ulos komponentistasi: ```js {1-4,13} const options = { @@ -905,13 +905,13 @@ function ChatRoom() { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Kaikki riippuvuudet määritelty // ... ``` -This way, you *prove* to the linter that it's not reactive. It can't change as a result of a re-render, so it doesn't need to be a dependency. Now re-rendering `ChatRoom` won't cause your Effect to re-synchronize. +Tällä tavalla *todistat* linterille, ettei se ole reaktiivinen. Se ei voi muuttua uudelleenrenderöinnin seurauksena, joten sen ei tarvitse olla riippuvuus. Nyt `ChatRoom` uudelleenrenderöinti ei aiheuta Efektisi uudelleensynkronointia. -This works for functions too: +Tämä toimii myös funktioille: ```js {1-6,12} function createOptions() { @@ -933,11 +933,11 @@ function ChatRoom() { // ... ``` -Since `createOptions` is declared outside your component, it's not a reactive value. This is why it doesn't need to be specified in your Effect's dependencies, and why it won't ever cause your Effect to re-synchronize. +Koska `createOptions` on määritelty komponentin ulkopuolella, se ei ole reaktiivinen arvo. Tämän takia sen ei tarvitse olla määritelty Efektisi riippuvuuksissa, eikä se koskaan aiheuta Efektisi uudelleensynkronointia. -#### Move dynamic objects and functions inside your Effect {/*move-dynamic-objects-and-functions-inside-your-effect*/} +#### Siirrä dynaamiset oliot ja funktiot Efektin sisään {/*move-dynamic-objects-and-functions-inside-your-effect*/} -If your object depends on some reactive value that may change as a result of a re-render, like a `roomId` prop, you can't pull it *outside* your component. You can, however, move its creation *inside* of your Effect's code: +Jos oliosi riippuu jostain reaktiivisesta arvosta, joka voi muuttua renderöinnin yhteydessä, kuten `roomId` propsi, et voi siirtää sitä komponentin ulkopuolelle. Voit kuitenkin siirtää sen luomisen koodin Efektisi sisälle: ```js {7-10,11,14} const serverUrl = 'https://localhost:1234'; @@ -957,20 +957,20 @@ function ChatRoom({ roomId }) { // ... ``` -Now that `options` is declared inside of your Effect, it is no longer a dependency of your Effect. Instead, the only reactive value used by your Effect is `roomId`. Since `roomId` is not an object or function, you can be sure that it won't be *unintentionally* different. In JavaScript, numbers and strings are compared by their content: +Nyt kun `options` on määritelty Efektisi sisällä, se ei ole enää Efektisi riippuvuus. Sen sijaan, ainoa Efektisi käyttämä reaktiivinen arvo on `roomId`. Koska `roomId` ei ole oli taikka funktio, voit olla varma ettei se ole *tahattomasti* eri. JavaScriptissa, numerot ja merkkijonot verrataan niiden sisällön perusteella: ```js {7-8} -// During the first render +// Ensimmäisen renderöinnin aikana const roomId1 = 'music'; -// During the next render +// Seuraavan renderöinnin aikana const roomId2 = 'music'; -// These two strings are the same! +// Nämä kaksi merkkijonoa vastaavat toisiaan! console.log(Object.is(roomId1, roomId2)); // true ``` -Thanks to this fix, the chat no longer re-connects if you edit the input: +Kiitos tämän korjauksen, chat ei enää yhdistä uudelleen jos muutat syöttölaatikkoa: @@ -1025,7 +1025,7 @@ export default function App() { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti return { connect() { console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); @@ -1044,9 +1044,9 @@ button { margin-left: 10px; } -However, it *does* re-connect when you change the `roomId` dropdown, as you would expect. +Kuitenkin, se *yhdistää* uudelleen kun vaihdat `roomId` pudotusvalikkosta, kuten odotit. -This works for functions, too: +Tämä toimii myös funktioille: ```js {7-12,14} const serverUrl = 'https://localhost:1234'; @@ -1070,11 +1070,11 @@ function ChatRoom({ roomId }) { // ... ``` -You can write your own functions to group pieces of logic inside your Effect. As long as you also declare them *inside* your Effect, they're not reactive values, and so they don't need to be dependencies of your Effect. +Voit kirjoittaa omia funktioita ryhmitelläksesi logiikkaa Efektisi sisällä. Niin kauan kuin määrittelet ne Efektisi *sisällä*, ne eivät ole reaktiivisia arvoja, ja näin ollen ne eivät tarvitse olla Efektisi riippuvuuksia. -#### Read primitive values from objects {/*read-primitive-values-from-objects*/} +#### Lue primitiiviset arvot oliosta {/*read-primitive-values-from-objects*/} -Sometimes, you may receive an object from props: +Joskus saatat saada objektin propseista: ```js {1,5,8} function ChatRoom({ options }) { @@ -1084,11 +1084,11 @@ function ChatRoom({ options }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [options]); // ✅ All dependencies declared + }, [options]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -The risk here is that the parent component will create the object during rendering: +Riskinä tässä on että vanhempi komponentti luo olion renderöinnin aikana: ```js {3-6} ``` -This would cause your Effect to re-connect every time the parent component re-renders. To fix this, read information from the object *outside* the Effect, and avoid having object and function dependencies: +Tämä aiheuttaisi Efektisi yhdistävän uudelleen joka kerta kun vanhempi komponentti renderöi uudelleen. Korjataksesi tämän, lue informaatio oliosta *Efektin ulkopuolella*, ja vältä olion ja funktion riippuvuuksia: ```js {4,7-8,12} function ChatRoom({ options }) { @@ -1114,15 +1114,15 @@ function ChatRoom({ options }) { }); connection.connect(); return () => connection.disconnect(); - }, [roomId, serverUrl]); // ✅ All dependencies declared + }, [roomId, serverUrl]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -The logic gets a little repetitive (you read some values from an object outside an Effect, and then create an object with the same values inside the Effect). But it makes it very explicit what information your Effect *actually* depends on. If an object is re-created unintentionally by the parent component, the chat would not re-connect. However, if `options.roomId` or `options.serverUrl` really are different, the chat would re-connect. +Logiikasta tulee toistuvaa (luet arvoja objektista Efektin ulkopuolella, ja sitten luot objektin samoilla arvoilla Efektin sisällä). Mutta se tekee hyvin eksplisiittiseksi minkä informaation Efektisi *oikeasti* riippuu. Jos objekti luodaan uudelleen tahattomasti vanhemman komponentin toimesta, chat ei yhdistä uudelleen. Kuitenkin, jos `options.roomId` tai `options.serverUrl` ovat todella erilaisia, chat yhdistää uudelleen. -#### Calculate primitive values from functions {/*calculate-primitive-values-from-functions*/} +#### Laske primitiiviset arvot funktioissa {/*calculate-primitive-values-from-functions*/} -The same approach can work for functions. For example, suppose the parent component passes a function: +Sama tapa voi toimia myös funktioille. Esimerkiksi, oletetaan että vanhempi komponentti välittää funktion: ```js {3-8} ``` -To avoid making it a dependency (and causing it to re-connect on re-renders), call it outside the Effect. This gives you the `roomId` and `serverUrl` values that aren't objects, and that you can read from inside your Effect: +Välttääksesi tekemästä siitä riippuvuuden (ja aiheuttamasta uudelleen yhdistämistä renderöinneissä), kutsu sitä Efektin ulkopuolella. Tämä antaa sinulle `roomId` ja `serverUrl` arvot, jotka eivät ole objekteja, ja joita voit lukea Efektisi sisältä: ```js {1,4} function ChatRoom({ getOptions }) { @@ -1150,36 +1150,36 @@ function ChatRoom({ getOptions }) { }); connection.connect(); return () => connection.disconnect(); - }, [roomId, serverUrl]); // ✅ All dependencies declared + }, [roomId, serverUrl]); // ✅ Kaikki riippuvuudet määritelty // ... ``` -This only works for [pure](/learn/keeping-components-pure) functions because they are safe to call during rendering. If your function is an event handler, but you don't want its changes to re-synchronize your Effect, [wrap it into an Effect Event instead.](#do-you-want-to-read-a-value-without-reacting-to-its-changes) +Tämä toimii ainoastaan [puhtaille](/learn/keeping-components-pure) funktioille, koska ne ovat turvallisia kutsua renderöinnin aikana. Jos fuktiosi on tapahtumankäsittelijä, mutta et halua sen muutosten synkronoivan Efektisi, [kääri se Efektitapahtumaan.](#do-you-want-to-read-a-value-without-reacting-to-its-changes) -- Dependencies should always match the code. -- When you're not happy with your dependencies, what you need to edit is the code. -- Suppressing the linter leads to very confusing bugs, and you should always avoid it. -- To remove a dependency, you need to "prove" to the linter that it's not necessary. -- If some code should run in response to a specific interaction, move that code to an event handler. -- If different parts of your Effect should re-run for different reasons, split it into several Effects. -- If you want to update some state based on the previous state, pass an updater function. -- If you want to read the latest value without "reacting" it, extract an Effect Event from your Effect. -- In JavaScript, objects and functions are considered different if they were created at different times. -- Try to avoid object and function dependencies. Move them outside the component or inside the Effect. +- Riippuvuuksien tulisi aina vastata koodia. +- Kun et ole tyytyväinen riippuvuuksiisi, mitä sinun tulee muokata on koodi. +- Linterin hiljentäminen johtaa hyvin hämmentäviin bugeihin, ja sinun tulisi aina välttää sitä. +- Poistaaksesi riippuvuuden, sinun täytyy "todistaa" linterille, että se ei ole tarpeellinen. +- Jos jokin koodi tulisi suorittaa vastauksena tiettyyn vuorovaikutukseen, siirrä koodi tapahtumankäsittelijään. +- Jos Efektisi eri osat tulisi suorittaa eri syistä, jaa se useaksi Efektiksi. +- Jos haluat päivittää jotain tilaa aikaisemman tilan perusteella, välitä päivitysfunktio. +- Jos haluat lukea viimeisimmän arvon "reagoimatta" siihen, luo Efektitapahtuma Efektistäsi. +- JavaScriptissä oliot ja funktiot ovat erilaisia jos ne on luotu eri aikoina. +- Pyri välttämään oliota ja funktioita riippuvuuksina. Siirrä ne komponentin ulkopuolelle tai Efektin sisälle. -#### Fix a resetting interval {/*fix-a-resetting-interval*/} +#### Korjaa nollautuva laskuri {/*fix-a-resetting-interval*/} -This Effect sets up an interval that ticks every second. You've noticed something strange happening: it seems like the interval gets destroyed and re-created every time it ticks. Fix the code so that the interval doesn't get constantly re-created. +Tämä Efekti asettaa laskurin joka laskee joka sekunti. Huomaat jotain outoa tapahtuvan: näyttää siltä että laskuri tuhotaan ja luodaan uudelleen joka kerta kun se laskee. Korjaa koodi niin että laskuri ei tuhoudu jatkuvasti. -It seems like this Effect's code depends on `count`. Is there some way to not need this dependency? There should be a way to update the `count` state based on its previous value without adding a dependency on that value. +Näyttää siltä, että Efektin koodi riippuu `count` tilamuuttujasta. Onko jotain tapa olla tarvitsematta tätä riippuvuutta? Pitäisi olla tapa päivittää `count` tilamuuttujaa sen edellisen arvon perusteella ilman että lisätään riippuvuutta siihen arvoon. @@ -1211,9 +1211,9 @@ export default function Timer() { -You want to update the `count` state to be `count + 1` from inside the Effect. However, this makes your Effect depend on `count`, which changes with every tick, and that's why your interval gets re-created on every tick. +Haluat päivittää `count` tilamuuttujaa arvoon `count + 1` Efektin sisältä. Tämä kuitenkin tekee Efektistäsi riippuvaisen `count` tilamuuttujasta, joka muuttuu joka tikillä, ja tämän takia laskurisi luodaan uudelleen joka tikillä. -To solve this, use the [updater function](/reference/react/useState#updating-state-based-on-the-previous-state) and write `setCount(c => c + 1)` instead of `setCount(count + 1)`: +Ratkaistaksesi tämän, käytä [päivitysfunktiota](/reference/react/useState#updating-state-based-on-the-previous-state) ja kirjoita `setCount(c => c + 1)` sen sijaan että kirjoittaisit `setCount(count + 1)`: @@ -1241,19 +1241,19 @@ export default function Timer() { -Instead of reading `count` inside the Effect, you pass a `c => c + 1` instruction ("increment this number!") to React. React will apply it on the next render. And since you don't need to read the value of `count` inside your Effect anymore, so you can keep your Effect's dependencies empty (`[]`). This prevents your Effect from re-creating the interval on every tick. +Sen sijaan että lukisit `count` tilamuuttujan Efektissä, välitä `c => c + 1` ohje ("kasvata tätä lukua!") Reactille. React soveltaa sitä seuraavalla renderöinnillä. Ja koska et enää tarvitse lukea `count` arvoa Efektissäsi, voit pitää Efektisi riippuvuudet tyhjinä (`[]`). Tämä estää Efektisi luomasta uudelleen laskuria joka tikillä. -#### Fix a retriggering animation {/*fix-a-retriggering-animation*/} +#### Korjaa uudelleen käynnistyvä animaatio {/*fix-a-retriggering-animation*/} -In this example, when you press "Show", a welcome message fades in. The animation takes a second. When you press "Remove", the welcome message immediately disappears. The logic for the fade-in animation is implemented in the `animation.js` file as plain JavaScript [animation loop.](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) You don't need to change that logic. You can treat it as a third-party library. Your Effect creates an instance of `FadeInAnimation` for the DOM node, and then calls `start(duration)` or `stop()` to control the animation. The `duration` is controlled by a slider. Adjust the slider and see how the animation changes. +Tässä esimerkissä, kun painat "Show", tervetuloviesti haalistuu näkyviin. Animaatio kestää sekunnin. Kun painat "Remove", tervetuloviesti katoaa välittömästi. Logiikka haalistumisanimaatiolle on toteutettu `animation.js` tiedostossa JavaScriptin [animaatiosilmukkana.](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) Sinun ei tarvitse muuttaa tätä logiikkaa. Voit käsitellä sitä kolmannen osapuolen kirjastona. Efektisi luo `FadeInAnimation` instanssin DOM noodille, ja kutsuu sitten `start(duration)` tai `stop()` kontrolloidakseen animaatiota. `duration` kontrolloidaan liukusäätimellä. Säädä liukusäädintä ja katso miten animaatio muuttuu. -This code already works, but there is something you want to change. Currently, when you move the slider that controls the `duration` state variable, it retriggers the animation. Change the behavior so that the Effect does not "react" to the `duration` variable. When you press "Show", the Effect should use the current `duration` on the slider. However, moving the slider itself should not by itself retrigger the animation. +Tämä koodi toimii jo, mutta haluat muuttaa jotain. Tällä hetkellä, kun liikutat liukusäädintä joka kontrolloi `duration` tilamuuttujaa, se uudelleenkäynnistää animaation. Muuta käytöstä niin että Efekti ei "reagoi" `duration` tilamuuttujaan. Kun painat "Show", Efekti käyttää nykyistä `duration` arvoa liukusäätimellä. Kuitenkin, liukusäätimen liikuttaminen itsessään ei saisi uudelleenkäynnistää animaatiota. -Is there a line of code inside the Effect that should not be reactive? How can you move non-reactive code out of the Effect? +Onko Efektissäsi rivi koodia jonka ei tulisi olla reaktiivista? Miten voit siirtää ei-reaktiivisen koodin Efektin ulkopuolelle? @@ -1382,7 +1382,7 @@ html, body { min-height: 300px; } -Your Effect needs to read the latest value of `duration`, but you don't want it to "react" to changes in `duration`. You use `duration` to start the animation, but starting animation isn't reactive. Extract the non-reactive line of code into an Effect Event, and call that function from your Effect. +Efektisi täytyy lukea viimeisin `duration` arvo, mutta et halua sen "reagoivan" `duration` tilamuuttujan muutoksiin. Käytät `duration` arvoa animaation käynnistämiseen, mutta animaation käynnistäminen ei ole reaktiivista. Siirrä ei-reaktiivinen koodirivi Efektitapahtumaan, ja kutsu sitä funktiota Efektistäsi. @@ -1509,15 +1509,15 @@ Effect Events like `onAppear` are not reactive, so you can read `duration` insid -#### Fix a reconnecting chat {/*fix-a-reconnecting-chat*/} +#### Korjaa uudelleen yhdistyvä chat {/*fix-a-reconnecting-chat*/} -In this example, every time you press "Toggle theme", the chat re-connects. Why does this happen? Fix the mistake so that the chat re-connects only when you edit the Server URL or choose a different chat room. +Tässä esimerkissä, joka kerta kun painat "Toggle theme", chat yhdistää uudelleen. Miksi näin käy? Korjaa ongelma siten, jotta chat yhdistää uudelleen vain kun muokkaat Server URL:ää tai valitset eri chat-huoneen. -Treat `chat.js` as an external third-party library: you can consult it to check its API, but don't edit it. +Käsittele `chat.js` tiedostoa kuin kolmannen osapuolen kirjastoa: voit konsultoida sitä tarkistaaksesi sen API:n, mutta älä muokkaa sitä. -There's more than one way to fix this, but ultimately you want to avoid having an object as your dependency. +On useita tapoja ratkaista tämä, mutta lopulta haluat välttää olion käyttämistä riippuvuutena. @@ -1611,9 +1611,9 @@ label, button { display: block; margin-bottom: 5px; } -Your Effect is re-running because it depends on the `options` object. Objects can be re-created unintentionally, you should try to avoid them as dependencies of your Effects whenever possible. +Efektisi ajetaan uudelleen koska se riippuu `options` oliosta. Olioita voi luoda vahingossa, ja niitä tulisi välttää Efektien riippuvuuksina aina kun mahdollista. -The least invasive fix is to read `roomId` and `serverUrl` right outside the Effect, and then make the Effect depend on those primitive values (which can't change unintentionally). Inside the Effect, create an object and it pass to `createConnection`: +Vähiten häiritsevä tapa korjata on lukea `roomId` ja `serverUrl` suoraan Efektin ulkopuolelta, ja tehdä Efektistä riippuvainen näistä primitiivisistä arvoista (jotka eivät voi muuttua tahattomasti). Efektin sisällä, luo olio ja välitä se `createConnection` funktiolle: @@ -1682,7 +1682,7 @@ export default function ChatRoom({ options }) { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti if (typeof serverUrl !== 'string') { throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); } @@ -1707,7 +1707,7 @@ label, button { display: block; margin-bottom: 5px; } -It would be even better to replace the object `options` prop with the more specific `roomId` and `serverUrl` props: +Olisi vielä parempi korvata `options` olio-propsi tarkemmilla `roomId` ja `serverUrl` propseilla: @@ -1773,7 +1773,7 @@ export default function ChatRoom({ roomId, serverUrl }) { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti if (typeof serverUrl !== 'string') { throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); } @@ -1798,25 +1798,25 @@ label, button { display: block; margin-bottom: 5px; } -Sticking to primitive props where possible makes it easier to optimize your components later. +Pysyminen primitiivisissä propseissa aina kun mahdollista tekee komponenttien optimoinnista helpompaa. -#### Fix a reconnecting chat, again {/*fix-a-reconnecting-chat-again*/} +#### Korjaa uudelleen yhdistyvä chat, uudestaan {/*fix-a-reconnecting-chat-again*/} -This example connects to the chat either with or without encryption. Toggle the checkbox and notice the different messages in the console when the encryption is on and off. Try changing the room. Then, try toggling the theme. When you're connected to a chat room, you will receive new messages every few seconds. Verify that their color matches the theme you've picked. +Tämä esimerkki yhdistää chatin joko salatusti tai ilman salausta. Kokeile vaihtaa valintaruutua ja huomaa erilaiset viestit konsolissa, kun salaus on päällä ja pois päältä. Kokeile vaihtaa huonetta. Sitten kokeile vaihtaa teemaa. Kun olet yhdistetty chat-huoneeseen, saat uusia viestejä muutaman sekunnin välein. Varmista, että niiden väri vastaa valitsemaasi teemaa. -In this example, the chat re-connects every time you try to change the theme. Fix this. After the fix, changing the theme should not re-connect the chat, but toggling encryption settings or changing the room should re-connect. +Tässä esimerkissä, chat yhdistää uudelleen joka kerta kun yrität vaihtaa teemaa. Korjaa tämä. Korjauksen jälkeen, teeman vaihtaminen ei saayhdistää chatia, mutta salauksen asetusten vaihtaminen tai huoneen vaihtaminen saa yhdistää. -Don't change any code in `chat.js`. Other than that, you can change any code as long as it results in the same behavior. For example, you may find it helpful to change which props are being passed down. +Älä muuta yhtään koodia `chat.js` tiedostossa. Muuten voit muuttaa mitä tahansa koodia, kunhan se johtaa samaan toimintaan. Esimerkiksi, saatat löytää hyödylliseksi muuttaa mitä propseja välitetään alaspäin. -You're passing down two functions: `onMessage` and `createConnection`. Both of them are created from scratch every time `App` re-renders. They are considered to be new values every time, which is why they re-trigger your Effect. +Välität kahta eri funktiota: `onMessage` ja `createConnection`. Molemmat luodaan alusta joka kerta kun `App` renderöidään uudelleen. Ne ovat uusia arvoja joka kerta, minkä takia ne laukaisevat uudelleen Effectisi. -One of these functions is an event handler. Do you know some way to call an event handler an Effect without "reacting" to the new values of the event handler function? That would come in handy! +Yksi näistä funktioista on tapahtumankäsittelijä. Tiedätkö tapoja kutsua tapahtumankäsittelijää Efektinä ilman että "reagoit" tapahtumankäsittelijän uusiin arvoihin? Se olisi hyödyllistä! -Another of these functions only exists to pass some state to an imported API method. Is this function really necessary? What is the essential information that's being passed down? You might need to move some imports from `App.js` to `ChatRoom.js`. +Toinen näistä funktioista on olemassa vain välittääkseen tilaa tuodulle API-metodille. Onko tämä funktio todella tarpeellinen? Mikä on olennainen tieto, joka välitetään alaspäin? Saatat joutua siirtämään joitain tuontia `App.js` tiedostosta `ChatRoom.js` tiedostoon. @@ -2031,11 +2031,11 @@ label, button { display: block; margin-bottom: 5px; } -There's more than one correct way to solve this, but here is one possible solution. +On useita tapoja ratkaista tämä, mutta tässä on yksi mahdollinen ratkaisu. -In the original example, toggling the theme caused different `onMessage` and `createConnection` functions to be created and passed down. Since the Effect depended on these functions, the chat would re-connect every time you toggle the theme. +Alkuperäisessä esimerkissä, teeman vaihtaminen aiheutti erilaisten `onMessage` ja `createConnection` funktioiden luomisen ja välittämisen alaspäin. Koska Effect riippui näistä funktioista, chat yhdisti uudelleen aina kun teeman vaihtoi. -To fix the problem with `onMessage`, you needed to wrap it into an Effect Event: +Ongelman korjaamiseksi `onMessage` piti kääriä Efektitapahtumaan: ```js {1,2,6} export default function ChatRoom({ roomId, createConnection, onMessage }) { @@ -2047,9 +2047,9 @@ export default function ChatRoom({ roomId, createConnection, onMessage }) { // ... ``` -Unlike the `onMessage` prop, the `onReceiveMessage` Effect Event is not reactive. This is why it doesn't need to be a dependency of your Effect. As a result, changes to `onMessage` won't cause the chat to re-connect. +Toisin kuin `onMessage` propsi, `onReceiveMessage` Efektitapahtuma ei ole reaktiivinen. Tämän vuoksi se ei tarvitse riippuvuutta Efektistä. Tämän seurauksena muutokset `onMessage` eivät aiheuta chatin uudelleen yhdistämistä. -You can't do the same with `createConnection` because it *should* be reactive. You *want* the Effect to re-trigger if the user switches between an encrypted and an unencryption connection, or if the user switches the current room. However, because `createConnection` is a function, you can't check whether the information it reads has *actually* changed or not. To solve this, instead of passing `createConnection` down from the `App` component, pass the raw `roomId` and `isEncrypted` values: +Et voi tehdä samaa `createConnection` funktion kanssa, koska sen *tulisi* olla reaktiivinen. *Haluat* Efektin käynnistyvän uudelleen jos käyttäjä vaihtaa salatun ja salaamattoman yhteyden välillä, tai jos käyttäjä vaihtaa nykyistä huonetta. Kuitenkin, koska `createConnection` on funktio, et voi tarkistaa onko sen lukema tieto *todella* muuttunut vai ei. Ratkaistaksesi tämän, sen sijaan että välittäisit `createConnection` alaspäin `App` komponentista, välitä raa'at `roomId` ja `isEncrypted` arvot: ```js {2-3} ``` -Now you can move the `createConnection` function *inside* the Effect instead of passing it down from the `App`: +Nyt voit siirtää `createConnection` funktion Efektin *sisälle* sen sijaan, että välittäisit sen alaspäin `App` komponentista: ```js {1-4,6,10-20} import { @@ -2087,11 +2087,11 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // ... ``` -After these two changes, your Effect no longer depends on any function values: +Näiden kahden muutosten jälkeen, Efektisi ei enää riipu mistään muusta funktio-arvosta: ```js {1,8,10,21} export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Reactive values - const onReceiveMessage = useEffectEvent(onMessage); // Not reactive + const onReceiveMessage = useEffectEvent(onMessage); // Ei reaktiivinen useEffect(() => { function createConnection() { @@ -2110,10 +2110,10 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Reacti connection.on('message', (msg) => onReceiveMessage(msg)); connection.connect(); return () => connection.disconnect(); - }, [roomId, isEncrypted]); // ✅ All dependencies declared + }, [roomId, isEncrypted]); // ✅ Kaikki riippuvuudet määritelty ``` -As a result, the chat re-connects only when something meaningful (`roomId` or `isEncrypted`) changes: +Lopputuloksena, chat yhdistää uudelleen vain kun jotain merkityksellistä (`roomId` tai `isEncrypted`) muuttuu: @@ -2223,7 +2223,7 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { ```js chat.js export function createEncryptedConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti if (typeof serverUrl !== 'string') { throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); } @@ -2264,7 +2264,7 @@ export function createEncryptedConnection({ serverUrl, roomId }) { } export function createUnencryptedConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Todellinen toteutus yhdistäisi palvelimeen oikeasti if (typeof serverUrl !== 'string') { throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); }