diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md
index 5d9554a1a..9e0160062 100644
--- a/src/content/learn/lifecycle-of-reactive-effects.md
+++ b/src/content/learn/lifecycle-of-reactive-effects.md
@@ -1,37 +1,37 @@
---
-title: 'Lifecycle of Reactive Effects'
+title: 'Reaktiivisten Efektien elinkaari'
---
-Effects have a different lifecycle from components. Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it. This cycle can happen multiple times if your Effect depends on props and state that change over time. React provides a linter rule to check that you've specified your Effect's dependencies correctly. This keeps your Effect synchronized to the latest props and state.
+Efekteilla on eri elinkaari komponenteista. Komponentit voivat mountata, päivittyä, tai un-mountata. Efekti voi tehdä vain kaksi asiaa: aloittaa synkronoimaan jotain, ja myöhemmin lopettaa synkronointi. Tämä sykli voi tapahtua useita kertoja, jos Efekti riippuu propseista ja tilasta, jotka muuttuvat ajan myötä. React tarjoaa linter-säännön, joka tarkistaa, että olet määrittänyt Efektin riippuvuudet oikein. Tämä pitää Efektisi synkronoituna viimeisimpiin proppseihin ja statukseen.
-- How an Effect's lifecycle is different from a component's lifecycle
-- How to think about each individual Effect in isolation
-- When your Effect needs to re-synchronize, and why
-- How your Effect's dependencies are determined
-- What it means for a value to be reactive
-- What an empty dependency array means
-- How React verifies your dependencies are correct with a linter
-- What to do when you disagree with the linter
+- Miten Efektin elinkaari eroaa komponentin elinkaaresta
+- Miten ajatella jokaista yksittäistä Efektia erillään
+- Milloin Efektisi täytyy synkronoida uudelleen ja miksi
+- Miten Effektisi riippuvuudet määritellään
+- Mitä tarkoittaa kun arvo on reaktiivinen
+- Mitä tyhjä riippuvuustaulukko tarkoittaa
+- Miten React tarkistaa rippuuksien oikeudellisuuden linterin avulla
+- Mitä tehdä kun olet eri mieltä linterin kanssa
-## The lifecycle of an Effect {/*the-lifecycle-of-an-effect*/}
+## Efektin elinkaari {/*the-lifecycle-of-an-effect*/}
-Every React component goes through the same lifecycle:
+Jokainen React komponentti käy läpi saman elinkaaren:
-- A component _mounts_ when it's added to the screen.
-- A component _updates_ when it receives new props or state, usually in response to an interaction.
-- A component _unmounts_ when it's removed from the screen.
+- Komponentti _mounttaa_ kun se lisätään näytölle.
+- Komponentti _päivittyy_ kun se saa uudet propsit tai tilan, yleensä vuorovaikutuksen seurauksena.
+- Komponentti _unmounttaa_ kun se poistetaan näytöltä.
-**It's a good way to think about components, but _not_ about Effects.** Instead, try to think about each Effect independently from your component's lifecycle. An Effect describes how to [synchronize an external system](/learn/synchronizing-with-effects) to the current props and state. As your code changes, synchronization will need to happen more or less often.
+**Tämä on hyvä tapa ajatella komponentteja, mutta _ei_ Efektejä.** Sen sijaan, yritä ajatella jokaista Efektiä erillään komponentin elinkaaresta. Efekti kuvaa miten [ulkoinen järjestelmä synkronoidaan](/learn/synchronizing-with-effects) nykyisten propsien ja tilan kanssa. Kun koodisi muuttuu, synkronointi täytyy tapahtua useammin tai harvemmin.
-To illustrate this point, consider this Effect connecting your component to a chat server:
+Kuvallistaaksemme tämän pointin, harkitse tätä Efektia, joka yhdistää komponenttisi chat-palvelimeen:
```js
const serverUrl = 'https://localhost:1234';
@@ -48,7 +48,7 @@ function ChatRoom({ roomId }) {
}
```
-Your Effect's body specifies how to **start synchronizing:**
+Efektisi runko määrittää miten **syknronointi aloitetaan:**
```js {2-3}
// ...
@@ -60,7 +60,7 @@ Your Effect's body specifies how to **start synchronizing:**
// ...
```
-The cleanup function returned by your Effect specifies how to **stop synchronizing:**
+Efektisi palauttama siivousfunktio määrittelee miten **synkronointi lopetetaan:**
```js {5}
// ...
@@ -72,19 +72,19 @@ The cleanup function returned by your Effect specifies how to **stop synchronizi
// ...
```
-Intuitively, you might think that React would **start synchronizing** when your component mounts and **stop synchronizing** when your component unmounts. However, this is not the end of the story! Sometimes, it may also be necessary to **start and stop synchronizing multiple times** while the component remains mounted.
+Intuitiivisesti, saatat ajatella Reactin **aloittavan synkronoinnin** kun komponenttisi mountataan ja **lopettavan synkronoinnin** kun komponenttisi unmountataan. Tämä ei kuitenkaan ole tilanne! Joksus, saattaa olla tarpeellista **aloittaa ja lopettaa synkronointi useita kertoja** kun komponentti pysyy mountattuna.
-Let's look at _why_ this is necessary, _when_ it happens, and _how_ you can control this behavior.
+Katsotaan _miksi_ tämä on tarpeellista, _milloin_ se tapahtuu_, ja _miten_ voit hallita sen toimintaa.
-Some Effects don't return a cleanup function at all. [More often than not,](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) you'll want to return one--but if you don't, React will behave as if you returned an empty cleanup function.
+Jotkin Efektit eivät suorita siivousfunktiota ollenkaan. [Useimmiten,](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) haluat palauttaa siivousfunktion--mutta jos et, React käyttäytyy kuin olisit palauttanut tyhjän siivousfunktion.
-### Why synchronization may need to happen more than once {/*why-synchronization-may-need-to-happen-more-than-once*/}
+### Miksi synkronointi voi tapahtua useammin kuin kerran {/*why-synchronization-may-need-to-happen-more-than-once*/}
-Imagine this `ChatRoom` component receives a `roomId` prop that the user picks in a dropdown. Let's say that initially the user picks the `"general"` room as the `roomId`. Your app displays the `"general"` chat room:
+Kuvittele, tämä `ChatRoom` komponetti saa `roomId` propin, jonka käyttäjä valitsee pudotusvalikosta. Oletetaan, että aluksi käyttäjä valitsee `"general"` huoneen `roomId`:ksi. Sovelluksesi näyttää `"general"` chat-huoneen:
```js {3}
const serverUrl = 'https://localhost:1234';
@@ -95,23 +95,23 @@ function ChatRoom({ roomId /* "general" */ }) {
}
```
-After the UI is displayed, React will run your Effect to **start synchronizing.** It connects to the `"general"` room:
+Kun UI on näytetty, React suorittaa Efektisi **aloittaakseen synkronoinnin.** Se yhdistää `"general"` huoneeseen:
```js {3,4}
function ChatRoom({ roomId /* "general" */ }) {
useEffect(() => {
- const connection = createConnection(serverUrl, roomId); // Connects to the "general" room
+ const connection = createConnection(serverUrl, roomId); // Yhdistää "general" huoneeseen
connection.connect();
return () => {
- connection.disconnect(); // Disconnects from the "general" room
+ connection.disconnect(); // Katkaisee yhteyden "general" huoneeseen
};
}, [roomId]);
// ...
```
-So far, so good.
+Tähän asti kaikki hyvin.
-Later, the user picks a different room in the dropdown (for example, `"travel"`). First, React will update the UI:
+Myöhemmin, käyttäjä valitsee eri huoneen pudotusvalikosta (esim. `"travel"`). Ensin, React päivittää UI:n:
```js {1}
function ChatRoom({ roomId /* "travel" */ }) {
@@ -120,93 +120,94 @@ function ChatRoom({ roomId /* "travel" */ }) {
}
```
-Think about what should happen next. The user sees that `"travel"` is the selected chat room in the UI. However, the Effect that ran the last time is still connected to the `"general"` room. **The `roomId` prop has changed, so what your Effect did back then (connecting to the `"general"` room) no longer matches the UI.**
+Ajattele mitä tulisi tapahtua seuraavaksi. Käyttäjä näkee, että `"travel"` on valittu chat-huoneeksi UI:ssa. Kuitenkin, Efekti joka viimeksi suoritettiin on yhä yhdistetty `"general"` huoneeseen. **`roomId` propsi on muuttunut, joten mitä Efektisi teki silloin (yhdisti `"general"` huoneeseen) ei enää vastaa UI:ta.**
-At this point, you want React to do two things:
+Tässä vaiheessa, haluat Reactin tekevän kaksi asiaa:
-1. Stop synchronizing with the old `roomId` (disconnect from the `"general"` room)
-2. Start synchronizing with the new `roomId` (connect to the `"travel"` room)
+1. Lopettaa synkronoinnin vanhan `roomId` kanssa (katkaisee yhteyden `"general"` huoneeseen)
+2. Aloittaa synkronoinnin uuden `roomId` kanssa (yhdistää `"travel"` huoneeseen)
-**Luckily, you've already taught React how to do both of these things!** Your Effect's body specifies how to start synchronizing, and your cleanup function specifies how to stop synchronizing. All that React needs to do now is to call them in the correct order and with the correct props and state. Let's see how exactly that happens.
+**Onneksi, olet jo opettanut Reactille miten teet molemmat näistä asioista!** Efektisi runko määrittää miten aloitat synkronoinnin, ja siivousfunktio määrittää miten lopetat synkronoinnin. Kaikki mitä Reactin täytyy tehdä nyt on kutsua niitä oikeassa järjestyksessä ja oikeilla propseilla ja tilalla. Katsotaan mitä oikein tapahtuu.
-### How React re-synchronizes your Effect {/*how-react-re-synchronizes-your-effect*/}
+### Miten React uudelleen synkronisoi Efektisi {/*how-react-re-synchronizes-your-effect*/}
-Recall that your `ChatRoom` component has received a new value for its `roomId` prop. It used to be `"general"`, and now it is `"travel"`. React needs to re-synchronize your Effect to re-connect you to a different room.
+Muista, että `ChatRoom` komponenttisi on saanut uuden arvon sen `roomId` propsiksi. Se olu aluksi `"general"`, ja se on nyt `"travel"`. Reactin täytyy synkronoida Efektisi uudelleen yhdistääkseen sinut eri huoneeseen.
+
+**Lopettaaksesi synkronoinnin,** React kutsuu siivousfunktiota, jonka Efektisi palautti yhdistettyään `"general"` huoneeseen. Koska `roomId` oli `"general"`, siivousfunktio katkaisee yhteyden `"general"` huoneeseen:
-To **stop synchronizing,** React will call the cleanup function that your Effect returned after connecting to the `"general"` room. Since `roomId` was `"general"`, the cleanup function disconnects from the `"general"` room:
```js {6}
function ChatRoom({ roomId /* "general" */ }) {
useEffect(() => {
- const connection = createConnection(serverUrl, roomId); // Connects to the "general" room
+ const connection = createConnection(serverUrl, roomId); // Yhdistää "general" huoneeseen
connection.connect();
return () => {
- connection.disconnect(); // Disconnects from the "general" room
+ connection.disconnect(); // Katkaisee yhteyden "general" huoneeseen
};
// ...
```
-Then React will run the Effect that you've provided during this render. This time, `roomId` is `"travel"` so it will **start synchronizing** to the `"travel"` chat room (until its cleanup function is eventually called too):
+React sitten kutsuu Efektiasi, jonka olet tarjonnut tämän renderöinnin aikana. Tällä kertaa, `roomId` on `"travel"` joten se **aloittaa synkronoinnin** `"travel"` chat-huoneeseen (kunnes sen siivousfunktio kutsutaan):
```js {3,4}
function ChatRoom({ roomId /* "travel" */ }) {
useEffect(() => {
- const connection = createConnection(serverUrl, roomId); // Connects to the "travel" room
+ const connection = createConnection(serverUrl, roomId); // Yhdistää "travel" huoneeseen
connection.connect();
// ...
```
-Thanks to this, you're now connected to the same room that the user chose in the UI. Disaster averted!
+Kiitos tämän, olet nyt yhdistetty samaan huoneeseen, jonka käyttäjä valitsi UI:ssa. Katastrofi vältetty!
-Every time after your component re-renders with a different `roomId`, your Effect will re-synchronize. For example, let's say the user changes `roomId` from `"travel"` to `"music"`. React will again **stop synchronizing** your Effect by calling its cleanup function (disconnecting you from the `"travel"` room). Then it will **start synchronizing** again by running its body with the new `roomId` prop (connecting you to the `"music"` room).
+Joka kerta kun komponenttisi renderöityy uudelleen eri `roomId`:llä, Efektisi täytyy synkronoida uudelleen. Esimerkiksi, sanotaan että käyttäjä muuttaa `roomId`:n arvosta `"travel"` arvoon `"music"`. Reactin täytyy taas **lopettaa synkronointi** Efektisi kanssa kutsumalla sen siivousfunktiota (katkaisemalla yhteys `"travel"` huoneeseen). Sitten se taas **aloittaa synkronoinnin** suorittamalla Efektisi rungon uudella `roomId` propsilla (yhdistämällä sinut `"music"` huoneeseen).
-Finally, when the user goes to a different screen, `ChatRoom` unmounts. Now there is no need to stay connected at all. React will **stop synchronizing** your Effect one last time and disconnect you from the `"music"` chat room.
+Lopuksi, kun käyttäjä menee eri ruutuun, `ChatRoom` unmounttaa. Sitten ei ole tarvetta pysyä ollenkaan yhteydessä. React **lopettaa synkronoinnin** Efektisi kanssa viimeisen kerran ja katkaisee yhteyden `"music"` huoneeseen.
-### Thinking from the Effect's perspective {/*thinking-from-the-effects-perspective*/}
+### Ajattelu Efektin perspektiivistä {/*thinking-from-the-effects-perspective*/}
-Let's recap everything that's happened from the `ChatRoom` component's perspective:
+Käydään läpi kaikki mitä tapahtui `ChatRoom` komponentin perspektiivissä:
-1. `ChatRoom` mounted with `roomId` set to `"general"`
-1. `ChatRoom` updated with `roomId` set to `"travel"`
-1. `ChatRoom` updated with `roomId` set to `"music"`
-1. `ChatRoom` unmounted
+1. `ChatRoom` mounttasi `roomId` arvolla `"general"`
+1. `ChatRoom` päivittyi `roomId` arvolla `"travel"`
+1. `ChatRoom` päivittyi `roomId` arvolla `"music"`
+1. `ChatRoom` unmounttasi
-During each of these points in the component's lifecycle, your Effect did different things:
+Jokaisen kohdan aikana komponentin elinkaaressa, Efektisi teki eri asioita:
-1. Your Effect connected to the `"general"` room
-1. Your Effect disconnected from the `"general"` room and connected to the `"travel"` room
-1. Your Effect disconnected from the `"travel"` room and connected to the `"music"` room
-1. Your Effect disconnected from the `"music"` room
+1. Efektisi yhdisti `"general"` huoneeseen
+1. Efektisi katkaisi yhteyden `"general"` huoneesta ja yhdisti `"travel"` huoneeseen
+1. Efektisi katkaisi yhteyden `"travel"` huoneesta ja yhdisti `"music"` huoneeseen
+1. Efektisi katkaisi yhteyden `"music"` huoneesta
-Now let's think about what happened from the perspective of the Effect itself:
+Ajatellaan mitä tapahtui Efektin perspektiivistä:
```js
useEffect(() => {
- // Your Effect connected to the room specified with roomId...
+ // Efektisi yhdisti huoneeseen, joka määriteltiin roomId:lla...
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
- // ...until it disconnected
+ // ...kunnes yhteys katkaistiin
connection.disconnect();
};
}, [roomId]);
```
-This code's structure might inspire you to see what happened as a sequence of non-overlapping time periods:
+Tämän koodin rakenne saattaa inspiroida sinua näkemään mitä tapahtui sekvenssinä ei-päällekkäisistä aikajaksoista:
-1. Your Effect connected to the `"general"` room (until it disconnected)
-1. Your Effect connected to the `"travel"` room (until it disconnected)
-1. Your Effect connected to the `"music"` room (until it disconnected)
+1. Efektisi yhdisti `"general"` huoneeseen (kunnes yhteys katkaistiin)
+1. Efektisi yhdisti `"travel"` huoneeseen (kunnes yhteys katkaistiin)
+1. Efektisi yhdisti `"music"` huoneeseen (kunnes yhteys katkaistiin)
-Previously, you were thinking from the component's perspective. When you looked from the component's perspective, it was tempting to think of Effects as "callbacks" or "lifecycle events" that fire at a specific time like "after a render" or "before unmount". This way of thinking gets complicated very fast, so it's best to avoid.
+Aiemmin, ajattelit komponentin perspektiivistä. Kun katsot sitä komponentin perspektiivistä, oli houkuttelevaa ajatella Efektejä "callbackeina" tai "elinkaari-tapahtumina", jotka tapahtuvat tiettyyn aikaan kuten "renderöinnin jälkeen" tai "ennen unmounttaamista". Tämä ajattelutapa monimutkaistuu nopeasti, joten on parempi välttää sitä.
-**Instead, always focus on a single start/stop cycle at a time. It shouldn't matter whether a component is mounting, updating, or unmounting. All you need to do is to describe how to start synchronization and how to stop it. If you do it well, your Effect will be resilient to being started and stopped as many times as it's needed.**
+**Sen sijaan, keskity aina yksittäiseen alku/loppu sykliin kerralla. Sillä ei tulisi olla merkitystä mounttaako, päivittyykö, vai unmounttaako komponentti. Sinun täytyy vain kuvailla miten aloitat synkronoinnin ja miten lopetat sen. Jos teet sen hyvin, Efektisi on kestävä aloittamiselle ja lopettamiselle niin monta kertaa kuin tarpeellista.**
-This might remind you how you don't think whether a component is mounting or updating when you write the rendering logic that creates JSX. You describe what should be on the screen, and React [figures out the rest.](/learn/reacting-to-input-with-state)
+Tämä saattaa muistuttaa sinua siitä, miten et ajattele mounttaako vai päivittyykö komponentti kun kirjoitat renderöintilogiikkaa, joka luo JSX:ää. Kuvailet mitä pitäisi olla näytöllä, ja React [selvittää loput.](/learn/reacting-to-input-with-state)
-### How React verifies that your Effect can re-synchronize {/*how-react-verifies-that-your-effect-can-re-synchronize*/}
+### Miten React vahvistaa, että Efektisi voi synkronoitua uudelleen {/*how-react-verifies-that-your-effect-can-re-synchronize*/}
-Here is a live example that you can play with. Press "Open chat" to mount the `ChatRoom` component:
+Tässä on esimerkki, jota voit kokeilla. Paina "Open chat" mountataksesi `ChatRoom` komponentin:
@@ -272,49 +273,49 @@ button { margin-left: 10px; }
-Notice that when the component mounts for the first time, you see three logs:
+Huomaa, että kun komponentti mounttaa ensimmäisen kerran, näet kolme lokia:
-1. `✅ Connecting to "general" room at https://localhost:1234...` *(development-only)*
-1. `❌ Disconnected from "general" room at https://localhost:1234.` *(development-only)*
+1. `✅ Connecting to "general" room at https://localhost:1234...` *(vain-kehitysvaiheessa)*
+1. `❌ Disconnected from "general" room at https://localhost:1234.` *(vain-kehitysvaiheessa)*
1. `✅ Connecting to "general" room at https://localhost:1234...`
-The first two logs are development-only. In development, React always remounts each component once.
+Ensimmäiset kaksi ovat vain kehityksessä. Kehityksessä, React uudelleen mounttaa jokaisen komponentin kerran.
-**React verifies that your Effect can re-synchronize by forcing it to do that immediately in development.** This might remind you of opening a door and closing it an extra time to check if the door lock works. React starts and stops your Effect one extra time in development to check [you've implemented its cleanup well.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)
+**React vahvistaa, että Efektisi voi synkronoitua uudelleen pakottamalla sen tekemään se välittömästi kehityksessä.** Tämä saattaa muistuttaa sinua oven avaamisesta ja sulkemisesta ylimääräisen kerran tarkistaaksesi, että lukko toimii. React aloittaa ja lopettaa Efektisi yhden ylimääräisen kerran kehityksessä tarkistaakseen, että [olet toteuttanut siivousfunktion hyvin.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)
-The main reason your Effect will re-synchronize in practice is if some data it uses has changed. In the sandbox above, change the selected chat room. Notice how, when the `roomId` changes, your Effect re-synchronizes.
+Pääsyy miksi Efektisi synkronoi uudelleen käytännössä on jos jokin data, jota se käyttää on muuttunut. Yllä olevassa hiekkalaatikossa, vaihda valittua chat-huonetta. Huomaa miten Efektisi synkronoituu uudelleen `roomId` muuttuessa.
-However, there are also more unusual cases in which re-synchronization is necessary. For example, try editing the `serverUrl` in the sandbox above while the chat is open. Notice how the Effect re-synchronizes in response to your edits to the code. In the future, React may add more features that rely on re-synchronization.
+Kuitenkin, on myös epätavallisempia tapauksia, joissa uudelleen synkronointi on tarpeellista. Esimerkiksi, kokeile muokata `serverUrl`:ää hiekkalaatikossa yllä kun chat on auki. Huomaa miten Efektisi synkronoituu uudelleen vastauksena koodin muokkaukseen. Tulevaisuudessa, React saattaa lisätä lisää ominaisuuksia, jotka nojaavat uudelleen synkronointiin.
-### How React knows that it needs to re-synchronize the Effect {/*how-react-knows-that-it-needs-to-re-synchronize-the-effect*/}
+### Miten React tietää, että sen täytyy synkronoida Efekti uudelleen {/*how-react-knows-that-it-needs-to-re-synchronize-the-effect*/}
-You might be wondering how React knew that your Effect needed to re-synchronize after `roomId` changes. It's because *you told React* that its code depends on `roomId` by including it in the [list of dependencies:](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies)
+Saatat miettiä miten React tiesi, että Efektisi täytyi synkronoida uudelleen `roomId`:n muuttuessa. Se johtuu siitä, että *kerroit Reactille* koodin riippuvan `roomId`:sta sisällyttämällä sen [riippuvuustaulukkoon:](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies)
```js {1,3,8}
-function ChatRoom({ roomId }) { // The roomId prop may change over time
+function ChatRoom({ roomId }) { // roomId propsi saattaa muuttua ajan kanssa
useEffect(() => {
- const connection = createConnection(serverUrl, roomId); // This Effect reads roomId
+ const connection = createConnection(serverUrl, roomId); // Tämä Efekti lukee roomId:n
connection.connect();
return () => {
connection.disconnect();
};
- }, [roomId]); // So you tell React that this Effect "depends on" roomId
+ }, [roomId]); // Joten kerrot Reactille, että tämä Efekti "riippuu" roomId:sta
// ...
```
-Here's how this works:
+Tässä miten tämä toimii:
-1. You knew `roomId` is a prop, which means it can change over time.
-2. You knew that your Effect reads `roomId` (so its logic depends on a value that may change later).
-3. This is why you specified it as your Effect's dependency (so that it re-synchronizes when `roomId` changes).
+1. Tiesit `roomId`:n olevan propsi, joka tarkoittaa, että se voi muuttua ajan kanssa.
+2. Tiesit, että Efektisi lukee `roomId`:n (joten sen logiikka riippuu arvosta, joka saattaa muuttua myöhemmin).
+3. Tämä on miksi määritit sen Efektisi riippuvuudeksi (jotta se synkronoituu uudelleen kun `roomId` muuttuu).
-Every time after your component re-renders, React will look at the array of dependencies that you have passed. If any of the values in the array is different from the value at the same spot that you passed during the previous render, React will re-synchronize your Effect.
+Joka kerta kun komponenttisi renderöityy uudelleen, React katsoo riippuvuustaulukkoa, jonka olet määrittänyt. Jos mikään arvoista taulukossa on eri kuin arvo samassa kohdassa, jonka annoit edellisellä renderöinnillä, React synkronoi Efektisi uudelleen.
-For example, if you passed `["general"]` during the initial render, and later you passed `["travel"]` during the next render, React will compare `"general"` and `"travel"`. These are different values (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), so React will re-synchronize your Effect. On the other hand, if your component re-renders but `roomId` has not changed, your Effect will remain connected to the same room.
+Esimerkiksi, jos välitit arvon `["general"]` ensimmäisen renderöinnin aikana, ja myöhemmin välitit `["travel"]` seuraavan renderöinnin aikana, React vertaa `"general"` ja `"travel"` arvoja. Nämä ovat eri arvoja (vertailtu [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) avulla), joten React synkronoi Efektisi uudelleen. Toisaalta, jos komponenttisi uudelleen renderöityy mutta `roomId` ei ole muuttunut, Efektisi pysyy yhdistettynä samaan huoneeseen.
-### Each Effect represents a separate synchronization process {/*each-effect-represents-a-separate-synchronization-process*/}
+### Kukin Efekti edustaa erillistä synkronointiprosessia {/*each-effect-represents-a-separate-synchronization-process*/}
-Resist adding unrelated logic to your Effect only because this logic needs to run at the same time as an Effect you already wrote. For example, let's say you want to send an analytics event when the user visits the room. You already have an Effect that depends on `roomId`, so you might feel tempted to add the analytics call there:
+Vältä lisäämästä aiheesta poikkeavaa logiikkaa Efektiisi vain koska tämä logiikka täytyy suorittaa samaan aikaan kuin Efekti, jonka olet jo kirjoittanut. Esimerkiksi, oletetaan että haluat lähettää analytiikka tapahtuman kun käyttäjä vierailee huoneessa. Sinulla on jo Efekti, joka riippuu `roomId`:sta, joten saatat tuntea houkutuksen lisätä analytiikka-kutsu sinne:
```js {3}
function ChatRoom({ roomId }) {
@@ -330,7 +331,7 @@ function ChatRoom({ roomId }) {
}
```
-But imagine you later add another dependency to this Effect that needs to re-establish the connection. If this Effect re-synchronizes, it will also call `logVisit(roomId)` for the same room, which you did not intend. Logging the visit **is a separate process** from connecting. Write them as two separate Effects:
+Mutta kuvittele, että myöhemmin lisäät toisen riippuvuuden tähän Efektiin, joka täytyy alustaa yhteys uudelleen. Jos tämä Efekti synkronoituu uudelleen, se kutsuu myös `logVisit(roomId)` samaan huoneeseen, jota et tarkoittanut. Vierailun kirjaaminen **on erillinen prosessi** yhdistämisestä. Kirjoita ne kahdeksi erilliseksi Efektiksi:
```js {2-4}
function ChatRoom({ roomId }) {
@@ -346,13 +347,13 @@ function ChatRoom({ roomId }) {
}
```
-**Each Effect in your code should represent a separate and independent synchronization process.**
+**Jokaisen Efektin koodissasi tulisi edustaa erillistä ja riippumatonta synkronointiprosessia.**
-In the above example, deleting one Effect wouldn’t break the other Effect's logic. This is a good indication that they synchronize different things, and so it made sense to split them up. On the other hand, if you split up a cohesive piece of logic into separate Effects, the code may look "cleaner" but will be [more difficult to maintain.](/learn/you-might-not-need-an-effect#chains-of-computations) This is why you should think whether the processes are same or separate, not whether the code looks cleaner.
+Yllä olevassa esimerkissä, yhden Efektin poistaminen ei hajota toisen Efektin logiikkaa. Tämä on hyvä indikaatio siitä, että ne synkronoivat eri asioita, joten oli järkevää jakaa ne kahteen erilliseen Efektiin. Toisaalta, jos jaat yhtenäisen logiikan eri Efekteihin, koodi saattaa näyttää "puhtaammalta" mutta tulee [vaikeammaksi ylläpitää.](/learn/you-might-not-need-an-effect#chains-of-computations) Tämän takia sinun tulisi ajatella ovatko prosessit samat vai erilliset, eivät sitä näyttääkö koodi puhtaammalta.
-## Effects "react" to reactive values {/*effects-react-to-reactive-values*/}
+## Efektit "reagoivat" reaktiivisiin arvoihin {/*effects-react-to-reactive-values*/}
-Your Effect reads two variables (`serverUrl` and `roomId`), but you only specified `roomId` as a dependency:
+Efektisi lukee kaksi muuttujaa (`serverUrl` ja `roomId`), mutta määritit vain `roomId`:n riippuvuudeksi:
```js {5,10}
const serverUrl = 'https://localhost:1234';
@@ -369,32 +370,32 @@ function ChatRoom({ roomId }) {
}
```
-Why doesn't `serverUrl` need to be a dependency?
+Miksei `serverUrl` tarvitse olla riippuvuus?
-This is because the `serverUrl` never changes due to a re-render. It's always the same no matter how many times the component re-renders and why. Since `serverUrl` never changes, it wouldn't make sense to specify it as a dependency. After all, dependencies only do something when they change over time!
+Tämä siksi, koska `serverUrl` ei muutu koskaan uudelleen renderöinnin seurauksena. Se on aina sama riippumatta kuinka monta kertaa komponentti renderöityy ja miksi. Koska `serverUrl` ei koskaan muutu, ei olisi järkevää määrittää sitä riippuvuudeksi. Loppujen lopuksi, riippuvuudet tekevät jotain vain kun ne muuttuvat ajan kanssa!
-On the other hand, `roomId` may be different on a re-render. **Props, state, and other values declared inside the component are _reactive_ because they're calculated during rendering and participate in the React data flow.**
+Toisella kädellä, `roomId` saattaa olla eri uudelleen renderöinnin seurauksena. **Propsit, tila, ja muut arvot, jotka on määritelty komponentin sisällä ovat _reaktiivisia_ koska ne lasketaan renderöinnin aikana ja osallistuvat Reactin datavirtaan.**
-If `serverUrl` was a state variable, it would be reactive. Reactive values must be included in dependencies:
+Jos `serverUrl` olisi tilamuuttuja, se olisi reaktiivinen. Reaktiiviset arvot täytyy sisällyttää riippuvuuksiin:
```js {2,5,10}
-function ChatRoom({ roomId }) { // Props change over time
- const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // State may change over time
+function ChatRoom({ roomId }) { // Propsit muuttuvat ajan kanssa
+ const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // Tila voi muuttua ajan kanssa
useEffect(() => {
- const connection = createConnection(serverUrl, roomId); // Your Effect reads props and state
+ const connection = createConnection(serverUrl, roomId); // Efektisi lukee propsin ja tilan
connection.connect();
return () => {
connection.disconnect();
};
- }, [roomId, serverUrl]); // So you tell React that this Effect "depends on" on props and state
+ }, [roomId, serverUrl]); // Joten kerrot Reactille, että tämä Efekti "riippuu" propsista ja tilasta
// ...
}
```
-By including `serverUrl` as a dependency, you ensure that the Effect re-synchronizes after it changes.
+Sisällyttämällä `serverUrl` riippuvuudeksi, varmistat että Efekti synkronoituu uudelleen sen muuttuessa.
-Try changing the selected chat room or edit the server URL in this sandbox:
+Kokeile muuttaa valittua chat-huonetta tai muokata server URL:ää tässä hiekkalaatikossa:
@@ -449,7 +450,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 + '...');
@@ -468,11 +469,11 @@ button { margin-left: 10px; }
-Whenever you change a reactive value like `roomId` or `serverUrl`, the Effect re-connects to the chat server.
+Aina kun muutat reaktiivista arvoa kuten `roomId` tai `serverUrl`, Efekti yhdistää uudelleen chat-palvelimeen.
-### What an Effect with empty dependencies means {/*what-an-effect-with-empty-dependencies-means*/}
+### Mitä tyhjä riippuvuustaulukko tarkoittaa {/*what-an-effect-with-empty-dependencies-means*/}
-What happens if you move both `serverUrl` and `roomId` outside the component?
+Mitä tapahtuu jos siirrät molemmat `serverUrl` ja `roomId` komponentin ulkopuolelle?
```js {1,2}
const serverUrl = 'https://localhost:1234';
@@ -485,14 +486,14 @@ function ChatRoom() {
return () => {
connection.disconnect();
};
- }, []); // ✅ All dependencies declared
+ }, []); // ✅ Kaikki riippuvuudet määritelty
// ...
}
```
-Now your Effect's code does not use *any* reactive values, so its dependencies can be empty (`[]`).
+Nyt Efektisi koodi ei käytä *yhtään* reaktiivista arvoa, joten sen riippuvuustaulukko voi olla tyhjä (`[]`).
-Thinking from the component's perspective, the empty `[]` dependency array means this Effect connects to the chat room only when the component mounts, and disconnects only when the component unmounts. (Keep in mind that React would still [re-synchronize it an extra time](#how-react-verifies-that-your-effect-can-re-synchronize) in development to stress-test your logic.)
+Ajattelu komponentin perspektiivista, tyhjä `[]` riippuvuustaulukko tarkoittaa, että tämä Efekti yhdistää chat-huoneeseen vain kun komponentti mounttaa, ja katkaisee yhteyden vain kun komponentti unmounttaa. (Pidä mielessä, että React silti [synkronoi ylimääräisen kerran](#how-react-verifies-that-your-effect-can-re-synchronize) kehityksessä testatakseen logiikkaasi.)
@@ -529,7 +530,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 + '...');
@@ -548,52 +549,52 @@ button { margin-left: 10px; }
-However, if you [think from the Effect's perspective,](#thinking-from-the-effects-perspective) you don't need to think about mounting and unmounting at all. What's important is you've specified what your Effect does to start and stop synchronizing. Today, it has no reactive dependencies. But if you ever want the user to change `roomId` or `serverUrl` over time (and they would become reactive), your Effect's code won't change. You will only need to add them to the dependencies.
+Kuitenkin, jos [ajattelet Efektin perspektiivista,](#thinking-from-the-effects-perspective) sinun ei tarvitse ajatella mountaamista ja unmountaamista ollenkaan. Tärkeää on se, että olet määritellyt mitä Efektisi tarvitsee synkronoinnin aloittamiseen ja lopettamiseen. Tänään, sillä ei ole yhtään reaktiivista riippuvuutta. Mutta jos haluat koskaan käyttäjän muuttavan `roomId`:n tai `serverUrl`:n ajan kanssa (ja ne tulisivat reaktiivisiksi), Efektisi koodi ei muutu. Sinun täytyy vain lisätä ne riippuvuuksiin.
-### All variables declared in the component body are reactive {/*all-variables-declared-in-the-component-body-are-reactive*/}
+### Kaikki muuttujat komponentin sisällä ovat reaktiivisia {/*all-variables-declared-in-the-component-body-are-reactive*/}
-Props and state aren't the only reactive values. Values that you calculate from them are also reactive. If the props or state change, your component will re-render, and the values calculated from them will also change. This is why all variables from the component body used by the Effect should be in the Effect dependency list.
+Propsit ja tila eivät ole ainoita reaktiivisia arvoja. Arvot jotka niistä lasket ovat myös reaktiivisia. Jos propsit tai tila muuttuu, komponenttisi tulee renderöitymään uudelleen, ja niistä lasketut arvot myös muuttuvat. Tämä on syy miksi kaikki Efektin tulisi lisätä Efektin riippuvuustaulukkoon kaikki komponentin sisällä olevat muuttujat, joita se käyttää.
-Let's say that the user can pick a chat server in the dropdown, but they can also configure a default server in settings. Suppose you've already put the settings state in a [context](/learn/scaling-up-with-reducer-and-context) so you read the `settings` from that context. Now you calculate the `serverUrl` based on the selected server from props and the default server:
+Sanotaan, että käyttäjä voi valita chat-palvelimen pudotusvalikosta, mutta he voivat myös määrittää oletuspalvelimen asetuksissa. Oletetaan, että olet jo laittanut asetukset -tilan [kontekstiin](/learn/scaling-up-with-reducer-and-context), joten luet `settings`:in siitä kontekstista. Nyt lasket `serverUrl`:n valitun palvelimen ja oletuspalvelimen perusteella:
```js {3,5,10}
-function ChatRoom({ roomId, selectedServerUrl }) { // roomId is reactive
- const settings = useContext(SettingsContext); // settings is reactive
- const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl is reactive
+function ChatRoom({ roomId, selectedServerUrl }) { // roomId on reaktiivinen
+ const settings = useContext(SettingsContext); // settings on reaktiivinen
+ const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl on reaktiivinen
useEffect(() => {
- const connection = createConnection(serverUrl, roomId); // Your Effect reads roomId and serverUrl
+ const connection = createConnection(serverUrl, roomId); // Efektisi lukee roomId ja serverUrl
connection.connect();
return () => {
connection.disconnect();
};
- }, [roomId, serverUrl]); // So it needs to re-synchronize when either of them changes!
+ }, [roomId, serverUrl]); // Joten sen täytyy synkronoida uudelleen kun jompi kumpi niistä muuttuu!
// ...
}
```
-In this example, `serverUrl` is not a prop or a state variable. It's a regular variable that you calculate during rendering. But it's calculated during rendering, so it can change due to a re-render. This is why it's reactive.
+Tässä esimerkissä, `serverUrl` ei ole propsi tai tilamuuttuja. Se on tavallinen muuttuja, jonka lasket renderöinnin aikana. Mutta se on laskettu renderöinnin aikana, jolloin se voi muuttua uudelleen renderöinnin seurauksena. Tämä on syy miksi se on reaktiivinen.
-**All values inside the component (including props, state, and variables in your component's body) are reactive. Any reactive value can change on a re-render, so you need to include reactive values as Effect's dependencies.**
+**Kaikki komponentin sisällä olevat arvot (mukaan lukien propsit, tila, ja muuttujat komponenttisi sisällä) ovat reaktiivisia. Mikä tahansa reaktiivinen arvo voi muuttua uudelleen renderöinnin seurauksena, joten sinun täytyy sisällyttää reaktiiviset arvot Efektin riippuvuustaulukkoon.**
-In other words, Effects "react" to all values from the component body.
+Toisin sanoen, Efektisi "reagoi" kaikkii arvoihin komponentin sisällä.
-#### Can global or mutable values be dependencies? {/*can-global-or-mutable-values-be-dependencies*/}
+#### Voiko globaalit tai mutatoitavat arvot olla riippuvuuksia? {/*can-global-or-mutable-values-be-dependencies*/}
-Mutable values (including global variables) aren't reactive.
+Mutatoitavat arovot (mukaan lukien globaalit muttujat) eivät ole reaktiivisia.
-**A mutable value like [`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) can't be a dependency.** It's mutable, so it can change at any time completely outside of the React rendering data flow. Changing it wouldn't trigger a re-render of your component. Therefore, even if you specified it in the dependencies, React *wouldn't know* to re-synchronize the Effect when it changes. This also breaks the rules of React because reading mutable data during rendering (which is when you calculate the dependencies) breaks [purity of rendering.](/learn/keeping-components-pure) Instead, you should read and subscribe to an external mutable value with [`useSyncExternalStore`.](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store)
+**Mutatoitava arvo kuten [`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) ei voi olla riippuvuus.** Se on mutatoitavissa, joten se voi muuttua koska vain täysin Reactin renderöinnin datavirtauksen ulkopuolella. Sen muuttaminen ei käynnistäisi komponenttisi uudelleenrenderöintiä. Tämän takia, vaikka määrittelisit sen riippuvuuksissasi, React *ei tietäisi* synkronoida Efektiasi uudelleen sen muuttuessa. Tämä myös rikkoo Reactin sääntöjä, koska mutatoitavan datan lukeminen renderöinnin aikana (joka on kun lasket riippuvuuksia) rikkoo [renderöinnin puhtauden.](/learn/keeping-components-pure) Sen sijaan, sinun tulisi lukea ja tilata ulkoisesta mutatoitavasta arvosta [`useSyncExternalStore`:n avulla.](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store)
-**A mutable value like [`ref.current`](/reference/react/useRef#reference) or things you read from it also can't be a dependency.** The ref object returned by `useRef` itself can be a dependency, but its `current` property is intentionally mutable. It lets you [keep track of something without triggering a re-render.](/learn/referencing-values-with-refs) But since changing it doesn't trigger a re-render, it's not a reactive value, and React won't know to re-run your Effect when it changes.
+**Mutatoitava arvo kuten [`ref.current`](/reference/react/useRef#reference) tai siitä luettavat asiat eivät myöskään voi olla riippuvuuksia.** `useRef`:n palauttama ref-objekti voi olla riippuvuus, mutta sen `current`-ominaisuus on tarkoituksella mutatoitava. Sen avulla voit [pitää kirjaa jostain ilman, että se käynnistää uudelleenrenderöinnin.](/learn/referencing-values-with-refs) Mutta koska sen muuttaminen ei käynnistä uudelleenrenderöintiä, se ei ole reaktiivinen arvo, eikä React tiedä synkronoida Efektiasi uudelleen sen muuttuessa.
-As you'll learn below on this page, a linter will check for these issues automatically.
+Kuten tulet oppimaan tällä sivulla, linter tulee myös tarkistamaan näitä ongelmia automaattisesti.
-### React verifies that you specified every reactive value as a dependency {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/}
+### React tarkistaa, että olet määrittänyt jokaisen reaktiivisen arvon riippuvuudeksi {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/}
-If your linter is [configured for React,](/learn/editor-setup#linting) it will check that every reactive value used by your Effect's code is declared as its dependency. For example, this is a lint error because both `roomId` and `serverUrl` are reactive:
+Jos linterisi on [konfiguroitu Reactille,](/learn/editor-setup#linting) se tarkistaa, että jokainen reaktiivinen arvo, jota Efektisi koodi käyttää on määritelty sen riippuvuudeksi. Esimerkiksi, tämä on linterin virhe koska molemmat `roomId` ja `serverUrl` ovat reaktiivisia:
@@ -601,14 +602,14 @@ If your linter is [configured for React,](/learn/editor-setup#linting) it will c
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
-function ChatRoom({ roomId }) { // roomId is reactive
- const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl is reactive
+function ChatRoom({ roomId }) { // roomId on reaktiivinen
+ const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl on reaktiivinen
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
- }, []); // <-- Something's wrong here!
+ }, []); // <-- Jokin on pielessä täällä!
return (
<>
@@ -648,7 +649,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 + '...');
@@ -667,41 +668,42 @@ button { margin-left: 10px; }
-This may look like a React error, but really React is pointing out a bug in your code. Both `roomId` and `serverUrl` may change over time, but you're forgetting to re-synchronize your Effect when they change. You will remain connected to the initial `roomId` and `serverUrl` even after the user picks different values in the UI.
+Tämä saattaa näyttää React-virheeltä, mutta oikeasti React näyttää bugin koodissasi. Molemmat `roomId` sekä `serverUrl` voivat muuttua ajan kanssa, mutta unohdat synkronoida Efektisi kun ne muuttuvat. Pysyt yhdistettynä alkuperäiseen `roomId` ja `serverUrl` vaikka käyttäjä valitsisi eri arvot käyttöliittymässä.
+
+Korjataksesi bugin, seuraa linterin ehdotusta määrittääksesi `roomId` ja `serverUrl` Efektisi riippuvuuksiksi:
-To fix the bug, follow the linter's suggestion to specify `roomId` and `serverUrl` as dependencies of your Effect:
```js {9}
-function ChatRoom({ roomId }) { // roomId is reactive
- const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl is reactive
+function ChatRoom({ roomId }) { // roomId on reaktiivinen
+ const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl on reaktiivinen
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
- }, [serverUrl, roomId]); // ✅ All dependencies declared
+ }, [serverUrl, roomId]); // ✅ Kaikki riippuvuudet määritelty
// ...
}
```
-Try this fix in the sandbox above. Verify that the linter error is gone, and the chat re-connects when needed.
+Kokeile tätä korjausta hiekkalaatikossa yllä. Varmista, että linterin virhe on poistunut, ja chat yhdistyy kun se on tarpeellista.
-In some cases, React *knows* that a value never changes even though it's declared inside the component. For example, the [`set` function](/reference/react/useState#setstate) returned from `useState` and the ref object returned by [`useRef`](/reference/react/useRef) are *stable*--they are guaranteed to not change on a re-render. Stable values aren't reactive, so you may omit them from the list. Including them is allowed: they won't change, so it doesn't matter.
+Jossain tapauksissa, React *tietää*, että arvo ei tule koskaan muuttumaan vaikka se on määritelty komponentin sisällä. Esimerkiksi, [`set`-funktio](/reference/react/useState#setstate) joka palautetaan `useState`:sta ja ref-objekti, joka palautetaan [`useRef`](/reference/react/useRef) ovat *stabiileja*--niiden on taattu olevan muuttumattomia uudelleen renderöinnin seurauksena. Stabiilit arvot eivät ole reaktiivisia, joten voit jättää ne pois listasta. Niiden sisällyttäminen on sallittua: ne eivät muutu, joten sillä ei ole väliä.
-### What to do when you don't want to re-synchronize {/*what-to-do-when-you-dont-want-to-re-synchronize*/}
+### Mitä tehdä kun et halua synkronoida uudelleen {/*what-to-do-when-you-dont-want-to-re-synchronize*/}
-In the previous example, you've fixed the lint error by listing `roomId` and `serverUrl` as dependencies.
+Edellisessä esimerkissä, olet korjannut linter-virheen listaamalla `roomId` ja `serverUrl` riippuvuuksina.
-**However, you could instead "prove" to the linter that these values aren't reactive values,** i.e. that they *can't* change as a result of a re-render. For example, if `serverUrl` and `roomId` don't depend on rendering and always have the same values, you can move them outside the component. Now they don't need to be dependencies:
+**Kuitenkin, voisit sen sijaan "todistaa" linterille, että nämä arvot eivät ole reaktiivisia arvoja,** eli että ne *eivät voi* muuttua uudelleen renderöinnin seurauksena. Esimerkiksi, jos `serverUrl` ja `roomId` eivät riipu renderöinnistä ja ovat aina samoja arvoja, voit siirtää ne komponentin ulkopuolelle. Nyt niiden ei tarvitse olla riippuvuuksia:
```js {1,2,11}
-const serverUrl = 'https://localhost:1234'; // serverUrl is not reactive
-const roomId = 'general'; // roomId is not reactive
+const serverUrl = 'https://localhost:1234'; // serverUrl ei ole reaktiivinen
+const roomId = 'general'; // roomId ei ole reaktiivinen
function ChatRoom() {
useEffect(() => {
@@ -710,80 +712,80 @@ function ChatRoom() {
return () => {
connection.disconnect();
};
- }, []); // ✅ All dependencies declared
+ }, []); // ✅ Kaikki riippuvuudet määritelty
// ...
}
```
-You can also move them *inside the Effect.* They aren't calculated during rendering, so they're not reactive:
+Voit myös siirtää ne *Efektin sisälle.* Niitä ei lasketa renderöinnin aikana, joten ne eivät ole reaktiivisia:
```js {3,4,10}
function ChatRoom() {
useEffect(() => {
- const serverUrl = 'https://localhost:1234'; // serverUrl is not reactive
- const roomId = 'general'; // roomId is not reactive
+ const serverUrl = 'https://localhost:1234'; // serverUrl ei ole reaktiivinen
+ const roomId = 'general'; // roomId ei ole reaktiivinen
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
- }, []); // ✅ All dependencies declared
+ }, []); // ✅ Kaikki riippuvuudet määritelty
// ...
}
```
-**Effects are reactive blocks of code.** They re-synchronize when the values you read inside of them change. Unlike event handlers, which only run once per interaction, Effects run whenever synchronization is necessary.
+**Efektit ovat reaktiivisia koodinpalasia.** Ne synkronoituvat uudelleen kun arvot, joita niiden sisällä luet muuttuvat. Toisin kuin tapahtumankäsittelijät, jotka suoritetaan vain kerran jokaista interaktiota kohden, Efektit suoritetaan aina kun synkronointi on tarpeellista.
-**You can't "choose" your dependencies.** Your dependencies must include every [reactive value](#all-variables-declared-in-the-component-body-are-reactive) you read in the Effect. The linter enforces this. Sometimes this may lead to problems like infinite loops and to your Effect re-synchronizing too often. Don't fix these problems by suppressing the linter! Here's what to try instead:
+**Et voi "päättää" riippuvuuksiasi.** Riippuvuutesi täytyy sisältää kaikki [reaktiiviset arvot](#all-variables-declared-in-the-component-body-are-reactive), joita luet Efektissa. Linter edellyttää tätä. Joskus tämä saattaa aiheuttaa ongelmia kuten loputtomia silmukoita ja Efektisi liian usein synkronoimisen. Älä korjaa näitä ongelmia hiljentämällä linter! Kokeile sen sijaan seuraavaa:
-* **Check that your Effect represents an independent synchronization process.** If your Effect doesn't synchronize anything, [it might be unnecessary.](/learn/you-might-not-need-an-effect) If it synchronizes several independent things, [split it up.](#each-effect-represents-a-separate-synchronization-process)
+* **Tarkista, että Efektisi edustaa yksittäistä synkronointiprosessia.** Jos Efektisi ei synkronoi mitään, [se saattaa olla turha.](/learn/you-might-not-need-an-effect) Jos se synkronoi useita yksittäisiä asioita, [jaa se osiin.](#each-effect-represents-a-separate-synchronization-process)
-* **If you want to read the latest value of props or state without "reacting" to it and re-synchronizing the Effect,** you can split your Effect into a reactive part (which you'll keep in the Effect) and a non-reactive part (which you'll extract into something called an _Effect Event_). [Read about separating Events from Effects.](/learn/separating-events-from-effects)
+* **Jos haluat lukea propsien tai tilan arvon ilman "reagointia" tai Efektin synkronoimista uudelleen,** voit jakaa Efektisi reaktiiviseen osaan (joka pidetään Efektissä) ja ei-reaktiiviseen osaan (joka erotetaan _Efektin tapahtumaksi_). [Lue lisää tapahtumien erottamisesta Efekteistä.](/learn/separating-events-from-effects)
-* **Avoid relying on objects and functions as dependencies.** If you create objects and functions during rendering and then read them from an Effect, they will be different on every render. This will cause your Effect to re-synchronize every time. [Read more about removing unnecessary dependencies from Effects.](/learn/removing-effect-dependencies)
+* **Vältä riippuvuutta olioista ja funktioista.** Jos luot olioita ja funktioita renderöinnin aikana ja luet niitä Efekteissä, ne ovat uusia joka renderöinnillä. Tämä aiheuttaa Efektisi synkronoimisen uudelleen joka kerta. [Lue lisää tarpeettomien riippuvuuksien poistamisesta Efekteistä.](/learn/removing-effect-dependencies)
-The linter is your friend, but its powers are limited. The linter only knows when the dependencies are *wrong*. It doesn't know *the best* way to solve each case. If the linter suggests a dependency, but adding it causes a loop, it doesn't mean the linter should be ignored. You need to change the code inside (or outside) the Effect so that that value isn't reactive and doesn't *need* to be a dependency.
+Linter on ystäväsi, mutta sen voimat ovat rajattuja. Linter tietää vain koska riippuvuutesi on *väärin*. Se ei tiedä *parasta tapaa* ratkaista jokaista tilannetta. Jos linter suosittelee riippuvuutta, mutta sen lisääminen aiheuttaa silmukan, se ei tarkoita, että linter tulisi sivuuttaa. Sinun täytyy muuttaa koodia Efektin sisällä (tai ulkopuolella) niin, että arvo ei ole reaktiivinen eikä *tarvitse* olla riippuvuus.
-If you have an existing codebase, you might have some Effects that suppress the linter like this:
+Jos sinulla on olemassaoleva 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ä tämän kaltaista linterin hiljentämistä:
// eslint-ignore-next-line react-hooks/exhaustive-deps
}, []);
```
-On the [next](/learn/separating-events-from-effects) [pages](/learn/removing-effect-dependencies), you'll learn how to fix this code without breaking the rules. It's always worth fixing!
+[Seuraavalla](/learn/separating-events-from-effects) [sivulla](/learn/removing-effect-dependencies), opit korjaamaan tämän koodin rikkomatta sääntöjä. Se on aina korjaamisen arvoista!
-- Components can mount, update, and unmount.
-- Each Effect has a separate lifecycle from the surrounding component.
-- Each Effect describes a separate synchronization process that can *start* and *stop*.
-- When you write and read Effects, think from each individual Effect's perspective (how to start and stop synchronization) rather than from the component's perspective (how it mounts, updates, or unmounts).
-- Values declared inside the component body are "reactive".
-- Reactive values should re-synchronize the Effect because they can change over time.
-- The linter verifies that all reactive values used inside the Effect are specified as dependencies.
-- All errors flagged by the linter are legitimate. There's always a way to fix the code to not break the rules.
+- Komponentit voivat mountata, päivittää, ja unmountata.
+- Jokaisella Efektilla on erillinen elinkaari ympäröivästä komponentista.
+- Jokainen Efekti kuvaa erillistä synkronointiprosessia, joka voi *alkaa* ja *loppua*.
+- Kun kirjoitat ja luet Efekteja, ajattele jokaisen yksittäisen Efektin perspektiivistä (miten aloittaa ja lopettaa synkronointi) sen sijaan, että ajattelisit komponentin perspektiivistä (miten se mounttaa, päivittyy, tai unmounttaa).
+- Komponentin sisällä määritellyt arvot ovat "reaktiivisia".
+- Reaktiivisten arvojen tulisi synkronoida Efekti uudelleen koska ne voivat muuttua ajan kanssa.
+- Linter vahvistaa, että kaikki Efektin sisällä käytetyt reaktiiviset arvot on määritelty riippuvuuksiksi.
+- Kaikki linterin virheet ovat todellisia. On aina tapa korjata koodi siten, ettei se riko sääntöjä.
-#### Fix reconnecting on every keystroke {/*fix-reconnecting-on-every-keystroke*/}
+#### Korjaa yhdistäminen jokaisella näppäinpainalluksella {/*fix-reconnecting-on-every-keystroke*/}
-In this example, the `ChatRoom` component connects to the chat room when the component mounts, disconnects when it unmounts, and reconnects when you select a different chat room. This behavior is correct, so you need to keep it working.
+Tässä esimerkissä, `ChatRoom` komponentti yhdistää chat-huoneeseen kun komponentti mounttaa, katkaisee yhteyden kun se unmounttaa, ja yhdistää uudelleen kun valitset eri chat-huoneen. Tämä käyttäytyminen on oikein, joten sinun täytyy pitää se toiminnassa.
-However, there is a problem. Whenever you type into the message box input at the bottom, `ChatRoom` *also* reconnects to the chat. (You can notice this by clearing the console and typing into the input.) Fix the issue so that this doesn't happen.
+Kuitenkin, on ongelma. Aina kun kirjoitat viestilaatikkoon alhaalla, `ChatRoom` *myös* yhdistää chatiin uudelleen. (Voit huomata tämän tyhjentämällä konsolin ja kirjoittamalla viestilaatikkoon.) Korjaa ongelma niin, ettei näin tapahdu.
-You might need to add a dependency array for this Effect. What dependencies should be there?
+Sinun täytyy ehkä lisätä riippuvuuslista tälle Efektille. Mitkä riippuvuudet tulisi olla listattuna?
@@ -839,7 +841,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 + '...');
@@ -860,7 +862,7 @@ button { margin-left: 10px; }
-This Effect didn't have a dependency array at all, so it re-synchronized after every re-render. First, add a dependency array. Then, make sure that every reactive value used by the Effect is specified in the array. For example, `roomId` is reactive (because it's a prop), so it should be included in the array. This ensures that when the user selects a different room, the chat reconnects. On the other hand, `serverUrl` is defined outside the component. This is why it doesn't need to be in the array.
+Tällä Efektilla ei ole riippuvuustaulukkoa ollenkaan, joten se synkronoituu uudelleen jokaisen renderöinnin jälkeen. Ensiksi, lisää riippuvuustaulukko. Sitten, varmista, että jokainen reaktiivinen arvo, jota Efekti käyttää on määritelty taulukossa. Esimerkiksi, `roomId` on reaktiivinen (koska se on propsi), joten sen tulisi olla mukana taulukossa. Tämä varmistaa, että kun käyttäjä valitsee eri huoneen, chat yhdistää uudelleen. Toisaalta, `serverUrl` on määritelty komponentin ulkopuolella. Tämän takia sen ei tarvitse olla taulukossa.
@@ -914,7 +916,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 + '...');
@@ -935,15 +937,15 @@ button { margin-left: 10px; }
-#### Switch synchronization on and off {/*switch-synchronization-on-and-off*/}
+#### Vaihda synkronointi päälle ja pois {/*switch-synchronization-on-and-off*/}
-In this example, an Effect subscribes to the window [`pointermove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event) event to move a pink dot on the screen. Try hovering over the preview area (or touching the screen if you're on a mobile device), and see how the pink dot follows your movement.
+Tässä esimerkissä, Efekti tilaa window:n [`pointermove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event) tapahtuman liikuttaakseen vaaleanpunaista pistettä näytöllä. Kokeile liikuttaa hiirtä esikatselualueen päällä (tai kosketa näyttöä jos olet mobiililaitteella), ja katso miten vaaleanpunainen piste seuraa liikettäsi.
-There is also a checkbox. Ticking the checkbox toggles the `canMove` state variable, but this state variable is not used anywhere in the code. Your task is to change the code so that when `canMove` is `false` (the checkbox is ticked off), the dot should stop moving. After you toggle the checkbox back on (and set `canMove` to `true`), the box should follow the movement again. In other words, whether the dot can move or not should stay synchronized to whether the checkbox is checked.
+Esimerkistä löytyy myös valintaruutu. Valintaruudun valinta muuttaa `canMove` tilamuuttujaa, mutta tätä tilamuuttujaa ei käytetä missään koodissa. Tehtäväsi on muuttaa koodia siten, jotta kun `canMove` on arvoltaan `false` (valintaruutu ei ole valittuna), pisteen tulisi lakata seuraamasta. Kun valitset valintaruudun takaisin päälle (ja asetat `canMove` arvoksi `true`), piste tulisi seurata liikettä jälleen. Toisin sanoen, onko pisteellä lupa liikkua vai ei tulisi pysyä synkronoituna valintaruudun valintaan.
-You can't declare an Effect conditionally. However, the code inside the Effect can use conditions!
+Et voi määritellä Efektia ehdollisesti. Kuitenkin, koodi Efektin sisällä voi käyttää ehtolauseita!
@@ -1001,7 +1003,7 @@ body {
-One solution is to wrap the `setPosition` call into an `if (canMove) { ... }` condition:
+Yksi ratkaisu on kääriä `setPosition` kutsu `if (canMove) { ... }` ehtolauseeseen:
@@ -1057,7 +1059,7 @@ body {
-Alternatively, you could wrap the *event subscription* logic into an `if (canMove) { ... }` condition:
+Vaihtoehtoisesti, voisit kääriä *tapahtumatilauksen* logiikan `if (canMove) { ... }` ehtolauseeseen:
@@ -1113,19 +1115,19 @@ body {
-In both of these cases, `canMove` is a reactive variable that you read inside the Effect. This is why it must be specified in the list of Effect dependencies. This ensures that the Effect re-synchronizes after every change to its value.
+Molemmissa näistä tavoista, `canMove` on reaktiivinen muuttuja, jonka luet Efektin sisällä. Tämän takia se täytyy määritellä Efektin riippuvuustaulukossa. Tämä takaa sen, että Efekti synkronoituu uudelleen jokaisen muutoksen jälkeen.
-#### Investigate a stale value bug {/*investigate-a-stale-value-bug*/}
+#### Selvitä vanhentuneen arvon bugi {/*investigate-a-stale-value-bug*/}
-In this example, the pink dot should move when the checkbox is on, and should stop moving when the checkbox is off. The logic for this has already been implemented: the `handleMove` event handler checks the `canMove` state variable.
+Tässä esimerkissä, vaaleanpunainen piste tulisi liikkua kun valintaruutu on valittuna, ja lopettaa liikkuminen kun valintaruutu ei ole valittuna. Logiikka tälle on jo toteutettu: `handleMove` tapahtumankäsittelijä tarkistaa `canMove` tilamuuttujan.
-However, for some reason, the `canMove` state variable inside `handleMove` appears to be "stale": it's always `true`, even after you tick off the checkbox. How is this possible? Find the mistake in the code and fix it.
+Kuitenkin, jostain syystä, `canMove` tilamuuttuja `handleMove`:n sisällä näyttää olevan "vanhentunut": se on aina `true`, vaikka valitsisit pois valintaruudun. Miten tämä on mahdollista? Etsi virhe koodista ja korjaa se.
-If you see a linter rule being suppressed, remove the suppression! That's where the mistakes usually are.
+Jos näet hiljennetyn linter-säännön, poista hiljennys! Sieltä virheet yleensä löytyvät.
@@ -1187,13 +1189,13 @@ body {
-The problem with the original code was suppressing the dependency linter. If you remove the suppression, you'll see that this Effect depends on the `handleMove` function. This makes sense: `handleMove` is declared inside the component body, which makes it a reactive value. Every reactive value must be specified as a dependency, or it can potentially get stale over time!
+Ongelma alkuperäisessä koodissa oli riippuvuuslinterin hiljentäminen. Jos poistat hiljennyksen, huomaat, että tämä Efekti riippuu `handleMove` funktiosta. Tämä on järkevää: `handleMove` on määritelty komponentin sisällä, joka tekee siitä reaktiivisen arvon. Jokainen reaktiivinen arvo täytyy määritellä riippuvuutena, tai se voi vanhentua ajan kanssa!
-The author of the original code has "lied" to React by saying that the Effect does not depend (`[]`) on any reactive values. This is why React did not re-synchronize the Effect after `canMove` has changed (and `handleMove` with it). Because React did not re-synchronize the Effect, the `handleMove` attached as a listener is the `handleMove` function created during the initial render. During the initial render, `canMove` was `true`, which is why `handleMove` from the initial render will forever see that value.
+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.
-**If you never suppress the linter, you will never see problems with stale values.** There are a few different ways to solve this bug, but you should always start by removing the linter suppression. Then change the code to fix the lint error.
+**Jos et koskaan hiljennä linteria, et koskaan näe ongelmia vanhentuneiden arvojen kanssa.** On useita tapoja ratkaista tämä ongelma, mutta sinun tulisi aina aloittaa poistamalla linterin hiljennys. Sitten muuttaa koodia korjataksesi linter-virheen.
-You can change the Effect dependencies to `[handleMove]`, but since it's going to be a newly defined function for every render, you might as well remove dependencies array altogether. Then the Effect *will* re-synchronize after every re-render:
+Voit muuttaa Efektin riippuvuudeksi `[handleMove]`, mutta koska se on uudelleen määritelty funktio joka renderöinnillä, voisit yhtä hyvin poistaa riippuvuuslistan kokonaan. Tällöin Efekti *tulee* synkronoitumaan uudelleen jokaisen renderöinnin jälkeen:
@@ -1250,9 +1252,9 @@ body {
-This solution works, but it's not ideal. If you put `console.log('Resubscribing')` inside the Effect, you'll notice that it resubscribes after every re-render. Resubscribing is fast, but it would still be nice to avoid doing it so often.
+Tämä ratkaisu toimii, mutta se ei ole ihanteellinen. Jos laitat `console.log('Resubscribing')` Efektin sisään, huomaat, että se tilaa uudelleen jokaisen renderöinnin jälkeen. Uudelleen tilaaminen on nopeaa, mutta olisi silti mukavaa välttää sitä niin usein.
-A better fix would be to move the `handleMove` function *inside* the Effect. Then `handleMove` won't be a reactive value, and so your Effect won't depend on a function. Instead, it will need to depend on `canMove` which your code now reads from inside the Effect. This matches the behavior you wanted, since your Effect will now stay synchronized with the value of `canMove`:
+Parempi ratkaisu olisi siirtää `handleMove` funktio Efektin *sisään*. Sitten `handleMove` ei olisi reaktiivinen arvo ja siten Efektisi ei riippuisi fuktiosta. Sen sijaan, se riippuu `canMove`sta, jonka koodisi lukee Efektin sisällä. Tämä vastaa haluttua käyttäytymistä, koska Efektisi pysyy nyt synkronoituna `canMove` arvon kanssa:
@@ -1309,21 +1311,21 @@ body {
-Try adding `console.log('Resubscribing')` inside the Effect body and notice that now it only resubscribes when you toggle the checkbox (`canMove` changes) or edit the code. This makes it better than the previous approach that always resubscribed.
+Kokeile lisätä `console.log('Resubscribing')` Efektin sisään ja huomaa, että nyt se tilaa uudelleen vain kun valitset valintaruudun (`canMove` muuttuu) tai muokkaat koodia. Tämä tekee siitä paremman kuin edellinen lähestymistapa, joka tilaa aina uudelleen.
-You'll learn a more general approach to this type of problem in [Separating Events from Effects.](/learn/separating-events-from-effects)
+Opit yleisemmän lähestymistavan tällaisiin ongelmiin [Tapahtumien erottaminen Efekteista](/learn/separating-events-from-effects) sivulla.
-#### Fix a connection switch {/*fix-a-connection-switch*/}
+#### Korjaa yhteyden kytkin {/*fix-a-connection-switch*/}
-In this example, the chat service in `chat.js` exposes two different APIs: `createEncryptedConnection` and `createUnencryptedConnection`. The root `App` component lets the user choose whether to use encryption or not, and then passes down the corresponding API method to the child `ChatRoom` component as the `createConnection` prop.
+Tässä esimerkissä, chat-palvelu `chat.js` tiedostossa tarjoaa kaksi erilaista APIa: `createEncryptedConnection` ja `createUnencryptedConnection`. Juuri `App` komponentti antaa käyttäjän valita käyttääkö salausta vai ei, ja sitten välittää vastaavan API-metodin `ChatRoom` alakomponentille `createConnection` propsina.
-Notice that initially, the console logs say the connection is not encrypted. Try toggling the checkbox on: nothing will happen. However, if you change the selected room after that, then the chat will reconnect *and* enable encryption (as you'll see from the console messages). This is a bug. Fix the bug so that toggling the checkbox *also* causes the chat to reconnect.
+Huomaa miten aluksi, konsoli sanoo, että yhteys ei ole salattu. Kokeile valintaruudun valitsemista: mikään ei tapahdu. Kuitenkin, jos vaihdat valittua huonetta tämän jälkeen, chat yhdistää uudelleen *ja* ottaa salauksen käyttöön (kuten konsoliviesteistä näet). Tämä on bugi. Korjaa bugi niin, että valintaruudun valitseminen aiheuttaa *myös* chatin yhdistämisen uudelleen.
-Suppressing the linter is always suspicious. Could this be a bug?
+Linterin hiljentäminen on aina epäilyttävää. Voisiko tämä olla bugi?
@@ -1391,7 +1393,7 @@ export default function ChatRoom({ roomId, createConnection }) {
```js chat.js
export function createEncryptedConnection(roomId) {
- // A real implementation would actually connect to the server
+ // Todellinen toteutus yhdistäisi palvelimeen oikeasti
return {
connect() {
console.log('✅ 🔐 Connecting to "' + roomId + '... (encrypted)');
@@ -1403,7 +1405,7 @@ export function createEncryptedConnection(roomId) {
}
export function createUnencryptedConnection(roomId) {
- // A real implementation would actually connect to the server
+ // Todellinen toteutus yhdistäisi palvelimeen oikeasti
return {
connect() {
console.log('✅ Connecting to "' + roomId + '... (unencrypted)');
@@ -1423,7 +1425,7 @@ label { display: block; margin-bottom: 10px; }
-If you remove the linter suppression, you will see a lint error. The problem is that `createConnection` is a prop, so it's a reactive value. It can change over time! (And indeed, it should--when the user ticks the checkbox, the parent component passes a different value of the `createConnection` prop.) This is why it should be a dependency. Include it in the list to fix the bug:
+Jos poistat linterin hiljennyksen, näet linter virheen. Ongelma on siinä, että `createConnection` on propsi, joten se on reaktiivinen arvo. Se voi muuttua ajan kanssa! (Ja tosiaan, sen tulisi--kun käyttäjä valitsee valintaruudun, vanhempi komponentti välittää eri arvon `createConnection` propsille.) Tämän takia sen tulisi olla riippuvuus. Lisää se listaan korjataksesi bugin:
@@ -1488,7 +1490,7 @@ export default function ChatRoom({ roomId, createConnection }) {
```js chat.js
export function createEncryptedConnection(roomId) {
- // A real implementation would actually connect to the server
+ // Todellinen toteutus yhdistäisi palvelimeen oikeasti
return {
connect() {
console.log('✅ 🔐 Connecting to "' + roomId + '... (encrypted)');
@@ -1500,7 +1502,7 @@ export function createEncryptedConnection(roomId) {
}
export function createUnencryptedConnection(roomId) {
- // A real implementation would actually connect to the server
+ // Todellinen toteutus yhdistäisi palvelimeen oikeasti
return {
connect() {
console.log('✅ Connecting to "' + roomId + '... (unencrypted)');
@@ -1518,7 +1520,7 @@ label { display: block; margin-bottom: 10px; }
-It is correct that `createConnection` is a dependency. However, this code is a bit fragile because someone could edit the `App` component to pass an inline function as the value of this prop. In that case, its value would be different every time the `App` component re-renders, so the Effect might re-synchronize too often. To avoid this, you can pass `isEncrypted` down instead:
+On oikein, että `createConnection` on riippuvuus. Kuitenkin, tämä koodi on hieman hauras sillä joku voisi muokata `App` komponenttia välittämään sisäisen funktion arvon tälle propsille. Tässä tapauksessa, sen arvo olisi eri joka kerta kun `App` komponentti renderöityy uudelleen, joten Efekti saattaisi synkronoitua liian usein. Välttääksesi tämän, voit välittää `isEncrypted` propsin sijaan:
@@ -1583,7 +1585,7 @@ export default function ChatRoom({ roomId, isEncrypted }) {
```js chat.js
export function createEncryptedConnection(roomId) {
- // A real implementation would actually connect to the server
+ // Todellinen toteutus yhdistäisi palvelimeen oikeasti
return {
connect() {
console.log('✅ 🔐 Connecting to "' + roomId + '... (encrypted)');
@@ -1595,7 +1597,7 @@ export function createEncryptedConnection(roomId) {
}
export function createUnencryptedConnection(roomId) {
- // A real implementation would actually connect to the server
+ // Todellinen toteutus yhdistäisi palvelimeen oikeasti
return {
connect() {
console.log('✅ Connecting to "' + roomId + '... (unencrypted)');
@@ -1613,21 +1615,21 @@ label { display: block; margin-bottom: 10px; }
-In this version, the `App` component passes a boolean prop instead of a function. Inside the Effect, you decide which function to use. Since both `createEncryptedConnection` and `createUnencryptedConnection` are declared outside the component, they aren't reactive, and don't need to be dependencies. You'll learn more about this in [Removing Effect Dependencies.](/learn/removing-effect-dependencies)
+Tässä versiossa, `App` komponentti välittää totuusarvo-propsin funktion sijaan. Efektin sisällä päätät mitä funktiota käyttää. Kerta molemmat `createEncryptedConnection` ja `createUnencryptedConnection` on määritelty komponentin ulkopuolella, ne eivät ole reaktiivisia, eivätkä niiden tarvitse olla riippuvuuksia. Opit tästä lisää [Efektin riippuvuuksien poistamisesta](/learn/removing-effect-dependencies)
-#### Populate a chain of select boxes {/*populate-a-chain-of-select-boxes*/}
+#### Täytä pudotusvalikkojen ketju {/*populate-a-chain-of-select-boxes*/}
-In this example, there are two select boxes. One select box lets the user pick a planet. Another select box lets the user pick a place *on that planet.* The second box doesn't work yet. Your task is to make it show the places on the chosen planet.
+Tässä esimerkissä on kaksi pudotusvalikkoa. Ensimmäinen pudotusvalikko antaa käyttäjän valita planeetan. Toinen pudotusvalikko antaa käyttäjän valita paikan *sillä planeetalla.* Toinen pudotusvalikko ei vielä toimi. Tehtäväsi on saada se näyttämään paikat valitulla planeetalla.
-Look at how the first select box works. It populates the `planetList` state with the result from the `"/planets"` API call. The currently selected planet's ID is kept in the `planetId` state variable. You need to find where to add some additional code so that the `placeList` state variable is populated with the result of the `"/planets/" + planetId + "/places"` API call.
+Katso miten ensimmäinen pudotusvalikko toimii. Se täyttää `planetList` tilan `"/planets"` API kutsun tuloksella. Tällä hetkellä valitun planeetan ID on `planetId` tilamuuttujassa. Sinun täytyy löytää kohta johon lisätä koodia, jotta `placeList` tilamuuttuja täyttyy `"/planets/" + planetId + "/places"` API kutsun tuloksella.
-If you implement this right, selecting a planet should populate the place list. Changing a planet should change the place list.
+Jos toteutat tämän oikein, planeetan valitsemisen tulisi täyttää paikkojen lista. Planeetan vaihtaminen tulisi vaihtaa paikkojen lista.
-If you have two independent synchronization processes, you need to write two separate Effects.
+Jos sinulla on kaksi synkronisointiprosessia, ne täytyy kirjoittaa kahteen erilliseen Efektiin.
@@ -1650,7 +1652,7 @@ export default function Page() {
if (!ignore) {
console.log('Fetched a list of planets.');
setPlanetList(result);
- setPlanetId(result[0].id); // Select the first planet
+ setPlanetId(result[0].id); // Valitse ensimmäinen planeetta
}
});
return () => {
@@ -1773,10 +1775,10 @@ label { display: block; margin-bottom: 10px; }
-There are two independent synchronization processes:
+On kaksi toisistaan riippumatonta synkronointiprosessia:
-- The first select box is synchronized to the remote list of planets.
-- The second select box is synchronized to the remote list of places for the current `planetId`.
+- Ensimmäinen pudotusvalikko on synkronoitu etäisten planeettojen listaan.
+- Toinen pudotusvalikko on synkronoitu etäisten paikkojen listaan nykyisellä `planetId`:llä.
This is why it makes sense to describe them as two separate Effects. Here's an example of how you could do this:
@@ -1799,7 +1801,7 @@ export default function Page() {
if (!ignore) {
console.log('Fetched a list of planets.');
setPlanetList(result);
- setPlanetId(result[0].id); // Select the first planet
+ setPlanetId(result[0].id); // Valitse ensimmäinen planeetta
}
});
return () => {
@@ -1809,7 +1811,7 @@ export default function Page() {
useEffect(() => {
if (planetId === '') {
- // Nothing is selected in the first box yet
+ // Mitään ei ole valittuna ensimmäisessä pudotusvalikossa vielä
return;
}
@@ -1818,7 +1820,7 @@ export default function Page() {
if (!ignore) {
console.log('Fetched a list of places on "' + planetId + '".');
setPlaceList(result);
- setPlaceId(result[0].id); // Select the first place
+ setPlaceId(result[0].id); // Valitse ensimmäinen paikka
}
});
return () => {
@@ -1939,9 +1941,9 @@ label { display: block; margin-bottom: 10px; }
-This code is a bit repetitive. However, that's not a good reason to combine it into a single Effect! If you did this, you'd have to combine both Effect's dependencies into one list, and then changing the planet would refetch the list of all planets. Effects are not a tool for code reuse.
+Tämä koodi on hieman toistuvaa. Kuitenkaan se ei ole hyvä syy yhdistää niitä yhteen Efektiin! Jos teit tämän, sinun täytyisi yhdistää molempien Efektien riippuvuudet yhdeksi listaksi, ja sitten planeetan vaihtaminen hakisikin listan kaikista planeetoista. Efektit eivät ole työkalu koodin uudelleenkäyttöön.
-Instead, to reduce repetition, you can extract some logic into a custom Hook like `useSelectOptions` below:
+Sen sijaan, välttääksesi toistoa, voit erottaa logiikan omaksi hookiksi kuten `useSelectOptions` alla:
@@ -2102,7 +2104,7 @@ label { display: block; margin-bottom: 10px; }
-Check the `useSelectOptions.js` tab in the sandbox to see how it works. Ideally, most Effects in your application should eventually be replaced by custom Hooks, whether written by you or by the community. Custom Hooks hide the synchronization logic, so the calling component doesn't know about the Effect. As you keep working on your app, you'll develop a palette of Hooks to choose from, and eventually you won't need to write Effects in your components very often.
+Katso `useSelectOptions.js` välilehti hiekkalaatikosta nähdäksesi miten se toimii. Ihanteellisesti, useimmat Efektit sovelluksessasi tulisi lopulta korvata omilla hookeilla, olivat ne sitten kirjoitettu sinun tai yhteisön toimesta. Omilla hookeilla piilotetaan synkronointilogiikka, joten kutsuva komponentti ei tiedä Efektistä. Kun jatkat sovelluksesi työstämistä, kehität paletin hookkeja joista valita, ja lopulta sinun ei tarvitse kirjoittaa Efektejä komponentteihisi kovin usein.