From 609e75a576b4fdfce79b69ed80035e6dc23cf0ac Mon Sep 17 00:00:00 2001 From: Arkadiusz Chatys Date: Thu, 14 Feb 2019 23:18:48 +0100 Subject: [PATCH 1/2] Lifting state up translated to Polish --- content/docs/lifting-state-up.md | 132 +++++++++++++++---------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/content/docs/lifting-state-up.md b/content/docs/lifting-state-up.md index 449330cfd..915300b2b 100644 --- a/content/docs/lifting-state-up.md +++ b/content/docs/lifting-state-up.md @@ -1,6 +1,6 @@ --- id: lifting-state-up -title: Lifting State Up +title: Wynoszenie stanu w górę permalink: docs/lifting-state-up.html prev: forms.html next: composition-vs-inheritance.html @@ -9,24 +9,24 @@ redirect_from: - "docs/flux-todo-list.html" --- -Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor. Let's see how this works in action. +Bardzo często, grupa komponentów musi odzwierciedlać te same zmiany w danych. W takim przypadku proponujemy przeniesienie wspólnego stanu do najbliższego wspólnego przodka. Zobaczmy, jak wygląda to w praktyce. -In this section, we will create a temperature calculator that calculates whether the water would boil at a given temperature. +W tej sekcji stworzymy kalkulator, który obliczy nam czy woda będzie się gotować w podanej temperaturze. -We will start with a component called `BoilingVerdict`. It accepts the `celsius` temperature as a prop, and prints whether it is enough to boil the water: +Rozpoczniemy od komponentu, który nazwiemy `BoilingVerdict`. Komponent ten przyjmie atrybut z temperaturą o nazwie `celcius`. Następnie zwróci on informację, czy temperatura jest wystarczająco wysoka. ```js{3,5} function BoilingVerdict(props) { if (props.celsius >= 100) { - return

The water would boil.

; + return

Woda będzie się gotować

; } - return

The water would not boil.

; + return

Woda nie będzie się gotować.

; } ``` -Next, we will create a component called `Calculator`. It renders an `` that lets you enter the temperature, and keeps its value in `this.state.temperature`. +Następnie, stworzymy komponent o nazwie `Calculator`. Wyrenderuje on ``, który pozwoli wpisać temperaturę oraz zachowa jego wartość w `this.state.temperature`. -Additionally, it renders the `BoilingVerdict` for the current input value. +Dodatkowo, będzie on renderował komponent `BoilingVerdict` dla obecnej wartości inputa. ```js{5,9,13,17-21} class Calculator extends React.Component { @@ -44,7 +44,7 @@ class Calculator extends React.Component { const temperature = this.state.temperature; return (
- Enter temperature in Celsius: + Podaj temperaturę w Celsjuszach: @@ -56,18 +56,18 @@ class Calculator extends React.Component { } ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/ZXeOBm?editors=0010) +[**Przetestuj kod na CodePen**](https://codepen.io/gaearon/pen/ZXeOBm?editors=0010) -## Adding a Second Input {#adding-a-second-input} +## Dodawanie drugiego inputa {#adding-a-second-input} -Our new requirement is that, in addition to a Celsius input, we provide a Fahrenheit input, and they are kept in sync. +Naszym kolejnym wymogiem jest, aby oprócz inputa do wpisywania temperatury w Celsjuszach, dostarczyć także input, który przyjmuje temperaturę w Fahrenheitach. Oba inputy powinny być ze sobą zsynchronizowane. -We can start by extracting a `TemperatureInput` component from `Calculator`. We will add a new `scale` prop to it that can either be `"c"` or `"f"`: +Rozpocznijmy od wyizolowania komponentu `TemperatureInput` z komponentu `Calculator`. Dodamy nowy atrybut `scale`, który będzie mógł przyjmować wartość `"c"` lub `"f"`: ```js{1-4,19,22} const scaleNames = { - c: 'Celsius', - f: 'Fahrenheit' + c: 'Celjuszach', + f: 'Fahrenheitach' }; class TemperatureInput extends React.Component { @@ -86,7 +86,7 @@ class TemperatureInput extends React.Component { const scale = this.props.scale; return (
- Enter temperature in {scaleNames[scale]}: + Podaj temperaturę w {scaleNames[scale]}:
@@ -95,7 +95,7 @@ class TemperatureInput extends React.Component { } ``` -We can now change the `Calculator` to render two separate temperature inputs: +Zmieńmy komponent `Calculator` tak, by renderował dwa osobne inputy z temperaturą: ```js{5,6} class Calculator extends React.Component { @@ -110,15 +110,15 @@ class Calculator extends React.Component { } ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/jGBryx?editors=0010) +[**Przetestuj kod na CodePen**](https://codepen.io/gaearon/pen/jGBryx?editors=0010) -We have two inputs now, but when you enter the temperature in one of them, the other doesn't update. This contradicts our requirement: we want to keep them in sync. +Mamy teraz dwa inputy, jednak podając temperaturę w jednym z nich, drugi nie zostanie zaktualizowany. Jest to sprzeczne z naszymi wymogami: chcemy, by oba inputy były ze sobą zsynchronizowane. -We also can't display the `BoilingVerdict` from `Calculator`. The `Calculator` doesn't know the current temperature because it is hidden inside the `TemperatureInput`. +Nie możemy też wyświetlić `BoilingVerdict` z komponentu `Calculator`. Spowodowane jest to faktem, iż `Calculator` nie ma dostępu do informacji o obecnej temperaturze, która schowana jest w `TemperatureInput`. -## Writing Conversion Functions {#writing-conversion-functions} +## Pisanie funkcji konwertujących {#writing-conversion-functions} -First, we will write two functions to convert from Celsius to Fahrenheit and back: +Na początek, napiszemy dwie funkcje do konwertowania temperatury ze stopni Celsjusza na Fahrenheita i z powrotem: ```js function toCelsius(fahrenheit) { @@ -130,9 +130,9 @@ function toFahrenheit(celsius) { } ``` -These two functions convert numbers. We will write another function that takes a string `temperature` and a converter function as arguments and returns a string. We will use it to calculate the value of one input based on the other input. +Obie te funkcje konwertują numery. Napiszemy kolejną funkcję, która przyjmie ciąg znaków `temperature` oraz funkcję konwertującą jako argumenty, zwracając ciąg znaków. Użyjemy tej funkcji do wyliczenia wartości jednego inputa, w oparciu o drugi. -It returns an empty string on an invalid `temperature`, and it keeps the output rounded to the third decimal place: +Funkcja zwróci zaokrąglony do trzeciego miejsca po przecinku wynik lub pusty ciąg znaków, jeśli `temperature` jest nieprawidłowe. ```js function tryConvert(temperature, convert) { @@ -146,11 +146,11 @@ function tryConvert(temperature, convert) { } ``` -For example, `tryConvert('abc', toCelsius)` returns an empty string, and `tryConvert('10.22', toFahrenheit)` returns `'50.396'`. +Na przykład, `tryConvert('abc', toCelcius)` zwróci pusty ciąg znaków, natomiast `tryConvert('10.22', toFahrenheit)` zwróci `'50.396'`. -## Lifting State Up {#lifting-state-up} +## Wynoszenie stanu w góre {#lifting-state-up} -Currently, both `TemperatureInput` components independently keep their values in the local state: +Obecnie, oba komponenty `TemperatureInput` trzymają swoją wartość niezależnie, w lokalnym stanie: ```js{5,9,13} class TemperatureInput extends React.Component { @@ -169,43 +169,43 @@ class TemperatureInput extends React.Component { // ... ``` -However, we want these two inputs to be in sync with each other. When we update the Celsius input, the Fahrenheit input should reflect the converted temperature, and vice versa. +Nam zależy jednak, aby oba te inputy były ze sobą zsynchronizowane. Podczas aktualizacji inputa Celsjusza, input Fahrenheita powinien odzwierciedlać przekonwertowaną temperaturę i odwrotnie. -In React, sharing state is accomplished by moving it up to the closest common ancestor of the components that need it. This is called "lifting state up". We will remove the local state from the `TemperatureInput` and move it into the `Calculator` instead. +W Reakcie, współdzielenie stanu komponentu można osiągnąć poprzez utworzenie wspólnego stanu w najbliższym wspólnym przodku. Nazywa się to "wynoszeniem stanu w górę" (ang. *Lifting state up*). -If the `Calculator` owns the shared state, it becomes the "source of truth" for the current temperature in both inputs. It can instruct them both to have values that are consistent with each other. Since the props of both `TemperatureInput` components are coming from the same parent `Calculator` component, the two inputs will always be in sync. +Jeśli wyniesiemy stan do komponentu `Calculator`, zostanie on "źródłem prawdy" dla obecnej temperatury w obu inputach. Komponent ten może poinformować je o trzymaniu zgodnych wartości. Oba inputy będą ze sobą zawsze zsynchronizowane, ponieważ atrybuty dla komponentów `TemperatureInput` przychodzą ze wspólnego przodka - komponentu `Calculator`. -Let's see how this works step by step. +Przeanalizujmy to krok po kroku. -First, we will replace `this.state.temperature` with `this.props.temperature` in the `TemperatureInput` component. For now, let's pretend `this.props.temperature` already exists, although we will need to pass it from the `Calculator` in the future: +Najpierw, zastąpimy `this.state.temperature` używając `this.props.temperature` w komponencie `TemperatureInput`. Na tę chwilę, przyjmijmy, że `this.props.temperature` istnieje, jednakże w przyszłości, będziemy musieli go przekazać z komponentu `Calculator`: ```js{3} render() { - // Before: const temperature = this.state.temperature; + // Wcześniej: const temperature = this.state.temperature; const temperature = this.props.temperature; // ... ``` -We know that [props are read-only](/docs/components-and-props.html#props-are-read-only). When the `temperature` was in the local state, the `TemperatureInput` could just call `this.setState()` to change it. However, now that the `temperature` is coming from the parent as a prop, the `TemperatureInput` has no control over it. +Wiemy, że [atrybuty są tylko do odczytu](/docs/components-and-props.html#props-are-read-only). Kiedy `temperature` znajdowało się w lokalnym stanie, komponent `TemperatureInput` mógł po prostu wywołać `this.setState()`, by je zmienić. Jednak, ponieważ teraz `temperature` jest przekazywane jako argument od rodzica, komponent `TemperatureInput` nie ma nad tym żadnej kontroli. -In React, this is usually solved by making a component "controlled". Just like the DOM `` accepts both a `value` and an `onChange` prop, so can the custom `TemperatureInput` accept both `temperature` and `onTemperatureChange` props from its parent `Calculator`. +W Reakcie, rozwiązujemy to na ogół poprzez ustanowienie komponentu "kontrolowanym". Tak samo, jak w DOMie, gdzie `` akceptuje zarówno `value`, jak i `onChange` jako atrybuty, tak i komponent `TemperatureInput` akceptuje atrybuty `temperature` oraz `onTemperatureChange` przekazywane przez rodzica. -Now, when the `TemperatureInput` wants to update its temperature, it calls `this.props.onTemperatureChange`: +Teraz, za każdym razem, gdy komponent `TemperatureInput` chce zaktualizować temperaturę, wywołuje on `this.props.onTemperatureChange`: ```js{3} handleChange(e) { - // Before: this.setState({temperature: e.target.value}); + // Wcześniej: this.setState({temperature: e.target.value}); this.props.onTemperatureChange(e.target.value); // ... ``` ->Note: +>Wskazówka: > ->There is no special meaning to either `temperature` or `onTemperatureChange` prop names in custom components. We could have called them anything else, like name them `value` and `onChange` which is a common convention. +> Nazwy, których używamy w przykładach, takie jak `temperature` czy `onTemperatureChange` są nazwami zwyczajowymi i można je określić w dowolny sposób. Moglibyśmy równie dobrze nazwać je `value` i `onChange`, co jest popularną konwencją. -The `onTemperatureChange` prop will be provided together with the `temperature` prop by the parent `Calculator` component. It will handle the change by modifying its own local state, thus re-rendering both inputs with the new values. We will look at the new `Calculator` implementation very soon. +Argumenty `onTemperatureChange` oraz `temperatur` zostaną przekazane przez rodzica - komponent `Calculator`. Obsłuży on zmianę poprzez zmodyfikowanie swojego stanu lokalnego, zatem oba inputy zostaną ponownie wyrenderowane z nowymi wartościami. Już wkrótce przyjrzymy się nowej implementacji komponentu `Calculator`. -Before diving into the changes in the `Calculator`, let's recap our changes to the `TemperatureInput` component. We have removed the local state from it, and instead of reading `this.state.temperature`, we now read `this.props.temperature`. Instead of calling `this.setState()` when we want to make a change, we now call `this.props.onTemperatureChange()`, which will be provided by the `Calculator`: +Zanim przyjrzymy się zmianom dokonanym komponentowi `Calculator`, podsumujmy zmiany w komponencie `TemperatureInput`. Pozbyliśmy się stanu lokalnego i zamiast korzystać z `this.state.temperature`, możemy teraz używać `this.props.temperature`. Zamiast wywoływać `this.setState()`, kiedy chcemy zrobić zmianę, możemy teraz odwołać się do `this.props.onTemperatureChange()`, które udostępnione zostanie przez komponent `Calculator`. ```js{8,12} class TemperatureInput extends React.Component { @@ -223,7 +223,7 @@ class TemperatureInput extends React.Component { const scale = this.props.scale; return (
- Enter temperature in {scaleNames[scale]}: + Podaj temperaturę w {scaleNames[scale]}:
@@ -232,11 +232,11 @@ class TemperatureInput extends React.Component { } ``` -Now let's turn to the `Calculator` component. +Przyjrzyjmy się teraz komponentowi `Calculator`. -We will store the current input's `temperature` and `scale` in its local state. This is the state we "lifted up" from the inputs, and it will serve as the "source of truth" for both of them. It is the minimal representation of all the data we need to know in order to render both inputs. +Wartości atrybutów obecnego inputa - `temperature` oraz `scale` będziemy przechowywać w stanie lokalnym. Użyjemy stanu, który "wynieśliśmy w górę" z inputów i będzie on nam służył za "źródło prawdy". Wynieśliśmy najmniejszą potrzebną ilość danych, które pozwolą nam renderować oba inputy. -For example, if we enter 37 into the Celsius input, the state of the `Calculator` component will be: +Na przykład, jeśli do inputa Celcius wpiszemy `37`, stan komponentu `Calculator` będzie wyglądał następująco: ```js { @@ -245,7 +245,7 @@ For example, if we enter 37 into the Celsius input, the state of the `Calculator } ``` -If we later edit the Fahrenheit field to be 212, the state of the `Calculator` will be: +Jeśli zdecydujemy się później edytować pole Fahrenheit i wpiszemy liczbę 212, stan komponentu `Calculator` będzie się przedstawiał tak: ```js { @@ -254,9 +254,9 @@ If we later edit the Fahrenheit field to be 212, the state of the `Calculator` w } ``` -We could have stored the value of both inputs but it turns out to be unnecessary. It is enough to store the value of the most recently changed input, and the scale that it represents. We can then infer the value of the other input based on the current `temperature` and `scale` alone. +Moglibyśmy przetrzymywać wartość obu inputów, jednak jest to kompletnie niepotrzebne. Trzymanie wartości ostatnio zmienianego inputa i jego skali wystarczy do uzyskania pożądanego wyniku. Możemy teraz wyliczyć wartość drugiego inputa na podstawie obecnej wartości `temperature` oraz `scale`. -The inputs stay in sync because their values are computed from the same state: +Inputy są ze sobą zsynchronizowane, ponieważ ich wartości są obliczane z tego samego stanu. ```js{6,10,14,18-21,27-28,31-32,34} class Calculator extends React.Component { @@ -299,32 +299,32 @@ class Calculator extends React.Component { } ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/WZpxpz?editors=0010) +[**Przetestuj kod na CodePen**](https://codepen.io/gaearon/pen/WZpxpz?editors=0010) -Now, no matter which input you edit, `this.state.temperature` and `this.state.scale` in the `Calculator` get updated. One of the inputs gets the value as is, so any user input is preserved, and the other input value is always recalculated based on it. +Nie ma teraz znaczenia, który z inputów edytujemy, w komponencie `Calculator` zarówno `this.state.temperature`, jak i `this.state.scale` są aktualizowane. -Let's recap what happens when you edit an input: +Podsumujmy teraz co dzieje się, gdy edytujemy jeden z inputów: -* React calls the function specified as `onChange` on the DOM ``. In our case, this is the `handleChange` method in the `TemperatureInput` component. -* The `handleChange` method in the `TemperatureInput` component calls `this.props.onTemperatureChange()` with the new desired value. Its props, including `onTemperatureChange`, were provided by its parent component, the `Calculator`. -* When it previously rendered, the `Calculator` has specified that `onTemperatureChange` of the Celsius `TemperatureInput` is the `Calculator`'s `handleCelsiusChange` method, and `onTemperatureChange` of the Fahrenheit `TemperatureInput` is the `Calculator`'s `handleFahrenheitChange` method. So either of these two `Calculator` methods gets called depending on which input we edited. -* Inside these methods, the `Calculator` component asks React to re-render itself by calling `this.setState()` with the new input value and the current scale of the input we just edited. -* React calls the `Calculator` component's `render` method to learn what the UI should look like. The values of both inputs are recomputed based on the current temperature and the active scale. The temperature conversion is performed here. -* React calls the `render` methods of the individual `TemperatureInput` components with their new props specified by the `Calculator`. It learns what their UI should look like. -* React calls the `render` method of the `BoilingVerdict` component, passing the temperature in Celsius as its props. -* React DOM updates the DOM with the boiling verdict and to match the desired input values. The input we just edited receives its current value, and the other input is updated to the temperature after conversion. +* React wywołuje funkcję `onChange` na elemencie DOM ``. W naszym przypadku jest to metoda `handleChange` z komponentu `TemperatureInput`. +* Metoda `handleChange` w komponencie `TemperatureInput` wywołuje `this.props.onTemperatureChange()` z nową wartością. Atrybuty tego komponentu, takie jak `onTemperatureChange` pochodzą od rodzica - komponentu `Calculator`. +* Podczas poprzedniego renderu, komponent `Calculator` określił, że atrybut `onTemperatureChange` przychodzący z komponentu `TemperatureInput` z Celsjuszami, jest wywoływany metodą `handleCelciusChange`, natomiast `onTemperatureChange` z komponentu z Fahrenheitami, jest teraz wywoływany metodą `handleFahrenheitChange`. Dzięki temu, w zależności od tego, który input dostanie nową wartość, jedna z tych metod zostanie wywołana. +* Wewnątrz tych metod, komponent `Calculator` prosi Reacta o ponowne renderowanie komponentu, poprzez użycie `this.setState()` z nową wartością inputa oraz obecnie używaną skalą. +* React wywołuje metodę `render` komponentu `Calculator`, by dowiedzieć się, jak powinien wyglądać jego UI. Wartości obu inputów są ponownie obliczane, bazując na aktualnych wartościach temperatury oraz aktywnej skali. To w tym miejscu odbywa się konwersja temperatury. +* React wywołuje metodę `render` indywidualnych komponentów `TemperatureInput` z ich nowymi atrybutami, które zostały określone przez komponent `Calculator`. React dowiaduje się wtedy jak powinien wyglądać UI komponentu. +* React wywołuje metodę `render` komponentu `BoilingVerdict`, przekazując do niego temperaturę w Celsjuszach jako atrybut. +* DOM Reacta aktualizuje DOM rezultatem dopasowując się do wartości inputów. Input, który właśnie edytowaliśmy, otrzymuje swoją obecną wartość, natomiast drugi input otrzymuje temperaturę po konwersji. -Every update goes through the same steps so the inputs stay in sync. +Każda aktualizacja przechodzi ten sam proces, więc inputy są zawsze zsynchronizowane. -## Lessons Learned {#lessons-learned} +## Wnioski {#lessons-learned} -There should be a single "source of truth" for any data that changes in a React application. Usually, the state is first added to the component that needs it for rendering. Then, if other components also need it, you can lift it up to their closest common ancestor. Instead of trying to sync the state between different components, you should rely on the [top-down data flow](/docs/state-and-lifecycle.html#the-data-flows-down). +Wszelkie dane, które zmieniają się w aplikacji reactowej, powinny mieć swoje pojedyncze "źródło prawdy". Na ogół, stan dodaje się najpierw do komponentu, który potrzebuje go podczas renderowania. Następnie, jeśli inny komponent potrzebuje tych samych danych, możemy je "wynieść w górę" do najbliższego wspólnego przodka. Zamiast prób synchronizacji stanu pomiędzy różnymi komponentami, powinieneś polegać na [przepływie danych "z góry na dół"](/docs/state-and-lifecycle.html#the-data-flows-down). -Lifting state involves writing more "boilerplate" code than two-way binding approaches, but as a benefit, it takes less work to find and isolate bugs. Since any state "lives" in some component and that component alone can change it, the surface area for bugs is greatly reduced. Additionally, you can implement any custom logic to reject or transform user input. +Wynoszenie stanu w górę wymaga pisanie bardziej "boilerplate'owo", niż wiązanie dwukierunkowe. Pozwala to jednak na łatwiejsze znalezienie i wyizolowanie błędów. Ponieważ każdy stan "żyje" w jakimś komponencie, a komponent ten może go zmienić, ryzyko powstania błędów jest znacznie mniejsze. Dodatkowo, można utworzyć dodatkową logikę, która odrzuci lub przeniesie dane wejściowe od użytkownika. -If something can be derived from either props or state, it probably shouldn't be in the state. For example, instead of storing both `celsiusValue` and `fahrenheitValue`, we store just the last edited `temperature` and its `scale`. The value of the other input can always be calculated from them in the `render()` method. This lets us clear or apply rounding to the other field without losing any precision in the user input. +Jeśli coś może być użyte zarówno z atrybutu, jak i stanu, prawdopodobnie nie powinno to pochodzić ze stanu. Na przykład, zamiast przetrzymywać zarówno `celciusValue` oraz `fahrenheitValue`, przechowujemy jedynie ostatnio edytowane `temperature` oraz `scale`. Wartość drugiego inputa może być przecież wyliczona podczas renderowania w metodzie `render()`. Pozwala nam to na edycję pozostałych pól, nie tracąc informacji przekazanej przez użytkownika. -When you see something wrong in the UI, you can use [React Developer Tools](https://github.com/facebook/react-devtools) to inspect the props and move up the tree until you find the component responsible for updating the state. This lets you trace the bugs to their source: +Jeśli zauważysz nieprawidłowości w UI swojej aplikacji, możesz użyć [Narzędzi Deweloperskich Reacta](https://github.com/facebook/react-devtools), by zbadać atrybuty i przemieszczać się po drzewie, dopóki nie znajdziesz komponentu odpowiedzialnego za zmianę stanu. Pozwoli Ci to znaleźć źródło błędów: -Monitoring State in React DevTools +Monitorowanie stanu w Narzędziach Deweloperskich Reacta From fc7da36d2115d4ac94061e8985f5ab4d07dcdb53 Mon Sep 17 00:00:00 2001 From: Arkadiusz Chatys Date: Sun, 17 Feb 2019 19:10:27 +0100 Subject: [PATCH 2/2] PR feedback changes for Lifting state up page translation --- content/docs/lifting-state-up.md | 82 ++++++++++++++++---------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/content/docs/lifting-state-up.md b/content/docs/lifting-state-up.md index 915300b2b..ba2ad4dfa 100644 --- a/content/docs/lifting-state-up.md +++ b/content/docs/lifting-state-up.md @@ -9,11 +9,11 @@ redirect_from: - "docs/flux-todo-list.html" --- -Bardzo często, grupa komponentów musi odzwierciedlać te same zmiany w danych. W takim przypadku proponujemy przeniesienie wspólnego stanu do najbliższego wspólnego przodka. Zobaczmy, jak wygląda to w praktyce. +Bardzo często kilka komponentów jednocześnie musi odzwierciedlać te same zmiany w danych. W takim przypadku proponujemy przeniesienie wspólnego stanu do najbliższego wspólnego przodka. Zobaczmy, jak wygląda to w praktyce. -W tej sekcji stworzymy kalkulator, który obliczy nam czy woda będzie się gotować w podanej temperaturze. +W tej części poradnika stworzymy kalkulator, który obliczy nam czy woda będzie się gotować w podanej temperaturze. -Rozpoczniemy od komponentu, który nazwiemy `BoilingVerdict`. Komponent ten przyjmie atrybut z temperaturą o nazwie `celcius`. Następnie zwróci on informację, czy temperatura jest wystarczająco wysoka. +Rozpocznijmy od komponentu, który nazwiemy `BoilingVerdict`. Komponent ten przyjmie atrybut z temperaturą o nazwie `celcius`, a następnie zwróci informację, czy temperatura jest wystarczająco wysoka. ```js{3,5} function BoilingVerdict(props) { @@ -24,7 +24,7 @@ function BoilingVerdict(props) { } ``` -Następnie, stworzymy komponent o nazwie `Calculator`. Wyrenderuje on ``, który pozwoli wpisać temperaturę oraz zachowa jego wartość w `this.state.temperature`. +Następnie stwórzmy komponent o nazwie `Calculator`. Wyrenderuje on element ``, który pozwoli wpisać temperaturę oraz zachowa jego wartość w `this.state.temperature`. Dodatkowo, będzie on renderował komponent `BoilingVerdict` dla obecnej wartości inputa. @@ -60,13 +60,13 @@ class Calculator extends React.Component { ## Dodawanie drugiego inputa {#adding-a-second-input} -Naszym kolejnym wymogiem jest, aby oprócz inputa do wpisywania temperatury w Celsjuszach, dostarczyć także input, który przyjmuje temperaturę w Fahrenheitach. Oba inputy powinny być ze sobą zsynchronizowane. +Naszym kolejnym wymogiem jest, aby oprócz inputa do wpisywania temperatury w Celsjuszach, dostarczyć także drugi input, który przyjmie temperaturę w Fahrenheitach. Oba inputy powinny być ze sobą zsynchronizowane. -Rozpocznijmy od wyizolowania komponentu `TemperatureInput` z komponentu `Calculator`. Dodamy nowy atrybut `scale`, który będzie mógł przyjmować wartość `"c"` lub `"f"`: +Zacznijmy od wyizolowania komponentu `TemperatureInput` z komponentu `Calculator`. Dodamy do niego nowy atrybut `scale`, który będzie mógł przyjmować wartość `"c"` lub `"f"`: ```js{1-4,19,22} const scaleNames = { - c: 'Celjuszach', + c: 'Celsjuszach', f: 'Fahrenheitach' }; @@ -112,13 +112,13 @@ class Calculator extends React.Component { [**Przetestuj kod na CodePen**](https://codepen.io/gaearon/pen/jGBryx?editors=0010) -Mamy teraz dwa inputy, jednak podając temperaturę w jednym z nich, drugi nie zostanie zaktualizowany. Jest to sprzeczne z naszymi wymogami: chcemy, by oba inputy były ze sobą zsynchronizowane. +Mamy teraz dwa inputy, jednak jeśli podamy temperaturę w jednym z nich, drugi nie zostanie zaktualizowany. Jest to sprzeczne z naszymi wymogami: chcemy, by oba inputy były ze sobą zsynchronizowane. -Nie możemy też wyświetlić `BoilingVerdict` z komponentu `Calculator`. Spowodowane jest to faktem, iż `Calculator` nie ma dostępu do informacji o obecnej temperaturze, która schowana jest w `TemperatureInput`. +Nie możemy też wyświetlić `BoilingVerdict` z poziomu komponentu `Calculator`. Spowodowane jest to faktem, iż `Calculator` nie ma dostępu do informacji o obecnej temperaturze, która schowana jest w `TemperatureInput`. ## Pisanie funkcji konwertujących {#writing-conversion-functions} -Na początek, napiszemy dwie funkcje do konwertowania temperatury ze stopni Celsjusza na Fahrenheita i z powrotem: +Na początek napiszmy dwie funkcje do konwertowania temperatury ze stopni Celsjusza na Fahrenheita i odwrotnie: ```js function toCelsius(fahrenheit) { @@ -130,7 +130,7 @@ function toFahrenheit(celsius) { } ``` -Obie te funkcje konwertują numery. Napiszemy kolejną funkcję, która przyjmie ciąg znaków `temperature` oraz funkcję konwertującą jako argumenty, zwracając ciąg znaków. Użyjemy tej funkcji do wyliczenia wartości jednego inputa, w oparciu o drugi. +Obie te funkcje konwertują liczby. Napiszmy jeszcze jedną funkcję, która jako argumenty przyjmie ciąg znaków `temperature` oraz funkcję konwertującą,, a zwróci inny ciąg znaków. Użyjemy jej do wyliczenia wartości jednego inputa w oparciu o drugi. Funkcja zwróci zaokrąglony do trzeciego miejsca po przecinku wynik lub pusty ciąg znaków, jeśli `temperature` jest nieprawidłowe. @@ -150,7 +150,7 @@ Na przykład, `tryConvert('abc', toCelcius)` zwróci pusty ciąg znaków, natomi ## Wynoszenie stanu w góre {#lifting-state-up} -Obecnie, oba komponenty `TemperatureInput` trzymają swoją wartość niezależnie, w lokalnym stanie: +Obecnie obydwa komponenty `TemperatureInput` trzymają swoje wartości niezależnie, w lokalnym stanie: ```js{5,9,13} class TemperatureInput extends React.Component { @@ -166,46 +166,46 @@ class TemperatureInput extends React.Component { render() { const temperature = this.state.temperature; - // ... + // ... ``` -Nam zależy jednak, aby oba te inputy były ze sobą zsynchronizowane. Podczas aktualizacji inputa Celsjusza, input Fahrenheita powinien odzwierciedlać przekonwertowaną temperaturę i odwrotnie. +Nam zależy jednak, aby oba te inputy były ze sobą zsynchronizowane. Podczas aktualizacji inputa z Celsjuszami, input z Fahrenheitami powinien odzwierciedlać przekonwertowaną temperaturę i odwrotnie. -W Reakcie, współdzielenie stanu komponentu można osiągnąć poprzez utworzenie wspólnego stanu w najbliższym wspólnym przodku. Nazywa się to "wynoszeniem stanu w górę" (ang. *Lifting state up*). +W Reakcie współdzielenie stanu komponentu można osiągnąć poprzez utworzenie stanu w najbliższym wspólnym przodku. Nazywa się to "wynoszeniem stanu w górę" (ang. *lifting state up*). Jeśli wyniesiemy stan do komponentu `Calculator`, zostanie on "źródłem prawdy" dla obecnej temperatury w obu inputach. Komponent ten może poinformować je o trzymaniu zgodnych wartości. Oba inputy będą ze sobą zawsze zsynchronizowane, ponieważ atrybuty dla komponentów `TemperatureInput` przychodzą ze wspólnego przodka - komponentu `Calculator`. Przeanalizujmy to krok po kroku. -Najpierw, zastąpimy `this.state.temperature` używając `this.props.temperature` w komponencie `TemperatureInput`. Na tę chwilę, przyjmijmy, że `this.props.temperature` istnieje, jednakże w przyszłości, będziemy musieli go przekazać z komponentu `Calculator`: +Najpierw zastąpimy `this.state.temperature` używając `this.props.temperature` w komponencie `TemperatureInput`. Na tę chwilę przyjmijmy, że atrybut `this.props.temperature` istnieje, jednakże w przyszłości będziemy musieli go przekazać z komponentu `Calculator`: ```js{3} render() { - // Wcześniej: const temperature = this.state.temperature; + // Wcześniej było: const temperature = this.state.temperature; const temperature = this.props.temperature; // ... ``` -Wiemy, że [atrybuty są tylko do odczytu](/docs/components-and-props.html#props-are-read-only). Kiedy `temperature` znajdowało się w lokalnym stanie, komponent `TemperatureInput` mógł po prostu wywołać `this.setState()`, by je zmienić. Jednak, ponieważ teraz `temperature` jest przekazywane jako argument od rodzica, komponent `TemperatureInput` nie ma nad tym żadnej kontroli. +Wiemy już, że [atrybuty są tylko do odczytu](/docs/components-and-props.html#props-are-read-only). Kiedy `temperature` znajdowało się w lokalnym stanie, komponent `TemperatureInput` mógł po prostu wywołać `this.setState()`, by je zmienić. Jednak, ponieważ teraz atrybut `temperature` jest przekazywany od rodzica, komponent `TemperatureInput` nie ma nad nim żadnej kontroli. -W Reakcie, rozwiązujemy to na ogół poprzez ustanowienie komponentu "kontrolowanym". Tak samo, jak w DOMie, gdzie `` akceptuje zarówno `value`, jak i `onChange` jako atrybuty, tak i komponent `TemperatureInput` akceptuje atrybuty `temperature` oraz `onTemperatureChange` przekazywane przez rodzica. +W Reakcie rozwiązujemy to na ogół poprzez przekształcenie komponentu w "kontrolowany". Tak samo jak w drzewie DOM, gdzie element `` akceptuje zarówno atrybut `value`, jak i `onChange` jako atrybuty, tak i komponent `TemperatureInput` może akceptować od rodzica atrybuty `temperature` oraz `onTemperatureChange`. -Teraz, za każdym razem, gdy komponent `TemperatureInput` chce zaktualizować temperaturę, wywołuje on `this.props.onTemperatureChange`: +Teraz za każdym razem, gdy komponent `TemperatureInput` zechce zaktualizować temperaturę, wywoła funkcję `this.props.onTemperatureChange`: ```js{3} handleChange(e) { - // Wcześniej: this.setState({temperature: e.target.value}); + // Wcześniej było: this.setState({temperature: e.target.value}); this.props.onTemperatureChange(e.target.value); // ... ``` >Wskazówka: > -> Nazwy, których używamy w przykładach, takie jak `temperature` czy `onTemperatureChange` są nazwami zwyczajowymi i można je określić w dowolny sposób. Moglibyśmy równie dobrze nazwać je `value` i `onChange`, co jest popularną konwencją. +> Nazwy, których używamy w tym poradniku, takie jak `temperature` czy `onTemperatureChange`, są tylko nazwami przykładowymi i można je zmienić w dowolny sposób. Jednak przyjęło się nazywać je np. `value` i `onChange`. -Argumenty `onTemperatureChange` oraz `temperatur` zostaną przekazane przez rodzica - komponent `Calculator`. Obsłuży on zmianę poprzez zmodyfikowanie swojego stanu lokalnego, zatem oba inputy zostaną ponownie wyrenderowane z nowymi wartościami. Już wkrótce przyjrzymy się nowej implementacji komponentu `Calculator`. +Atrybuty `onTemperatureChange` oraz `temperature` zostaną przekazane przez rodzica - komponent `Calculator`. Obsłuży on zmianę poprzez zmodyfikowanie swojego stanu lokalnego, zatem oba inputy zostaną ponownie wyrenderowane z nowymi wartościami. Już wkrótce przyjrzymy się nowej implementacji komponentu `Calculator`. -Zanim przyjrzymy się zmianom dokonanym komponentowi `Calculator`, podsumujmy zmiany w komponencie `TemperatureInput`. Pozbyliśmy się stanu lokalnego i zamiast korzystać z `this.state.temperature`, możemy teraz używać `this.props.temperature`. Zamiast wywoływać `this.setState()`, kiedy chcemy zrobić zmianę, możemy teraz odwołać się do `this.props.onTemperatureChange()`, które udostępnione zostanie przez komponent `Calculator`. +Zanim jednak zagłębimy się w zmiany w komponencie `Calculator`, podsumujmy co zmieniło się w `TemperatureInput`. Pozbyliśmy się stanu lokalnego i zamiast korzystać z `this.state.temperature`, możemy teraz używać `this.props.temperature`. Zamiast wywoływać `this.setState()`, kiedy chcemy dokonać zmiany, możemy teraz wywołać `this.props.onTemperatureChange()`, które udostępniane jest przez komponent `Calculator`. ```js{8,12} class TemperatureInput extends React.Component { @@ -234,9 +234,9 @@ class TemperatureInput extends React.Component { Przyjrzyjmy się teraz komponentowi `Calculator`. -Wartości atrybutów obecnego inputa - `temperature` oraz `scale` będziemy przechowywać w stanie lokalnym. Użyjemy stanu, który "wynieśliśmy w górę" z inputów i będzie on nam służył za "źródło prawdy". Wynieśliśmy najmniejszą potrzebną ilość danych, które pozwolą nam renderować oba inputy. +Wartości atrybutów obecnego inputa - `temperature` oraz `scale` będziemy przechowywać w stanie lokalnym. Użyjemy stanu, który "wynieśliśmy w górę" z inputów i będzie on nam służył za "źródło prawdy". Zarazem jest to najmniejsza ilość danych, wystarczająca do wyrenderować obydwóch inputów. -Na przykład, jeśli do inputa Celcius wpiszemy `37`, stan komponentu `Calculator` będzie wyglądał następująco: +Na przykład, jeśli do inputa z Celsjuszami wpiszemy `37`, stan komponentu `Calculator` będzie wyglądał następująco: ```js { @@ -245,7 +245,7 @@ Na przykład, jeśli do inputa Celcius wpiszemy `37`, stan komponentu `Calculato } ``` -Jeśli zdecydujemy się później edytować pole Fahrenheit i wpiszemy liczbę 212, stan komponentu `Calculator` będzie się przedstawiał tak: +Jeśli zdecydujemy się później edytować pole z Fahrenheitami i wpiszemy liczbę `212`, stan komponentu `Calculator` będzie się przedstawiał tak: ```js { @@ -254,9 +254,9 @@ Jeśli zdecydujemy się później edytować pole Fahrenheit i wpiszemy liczbę 2 } ``` -Moglibyśmy przetrzymywać wartość obu inputów, jednak jest to kompletnie niepotrzebne. Trzymanie wartości ostatnio zmienianego inputa i jego skali wystarczy do uzyskania pożądanego wyniku. Możemy teraz wyliczyć wartość drugiego inputa na podstawie obecnej wartości `temperature` oraz `scale`. +Moglibyśmy przechowywać wartości dla obydwóch inputów, jednak jest to zupełnie zbędne. Trzymanie wartości ostatnio zmienianego inputa i jego skali wystarczy do uzyskania pożądanego wyniku. Możemy teraz wyliczyć wartość drugiego inputa na podstawie obecnej wartości `temperature` oraz `scale`. -Inputy są ze sobą zsynchronizowane, ponieważ ich wartości są obliczane z tego samego stanu. +Inputy są ze sobą zsynchronizowane, dzięki temu, że ich wartości są obliczane na podstawie tego samego stanu. ```js{6,10,14,18-21,27-28,31-32,34} class Calculator extends React.Component { @@ -301,30 +301,30 @@ class Calculator extends React.Component { [**Przetestuj kod na CodePen**](https://codepen.io/gaearon/pen/WZpxpz?editors=0010) -Nie ma teraz znaczenia, który z inputów edytujemy, w komponencie `Calculator` zarówno `this.state.temperature`, jak i `this.state.scale` są aktualizowane. +Nie ważne teraz, który z inputów edytujemy, w komponencie `Calculator` aktualizowane będą zarówno `this.state.temperature`, jak i `this.state.scale`. -Podsumujmy teraz co dzieje się, gdy edytujemy jeden z inputów: +Podsumujmy teraz, co dzieje się, gdy edytujemy jeden z inputów: * React wywołuje funkcję `onChange` na elemencie DOM ``. W naszym przypadku jest to metoda `handleChange` z komponentu `TemperatureInput`. -* Metoda `handleChange` w komponencie `TemperatureInput` wywołuje `this.props.onTemperatureChange()` z nową wartością. Atrybuty tego komponentu, takie jak `onTemperatureChange` pochodzą od rodzica - komponentu `Calculator`. +* Metoda `handleChange` w komponencie `TemperatureInput` wywołuje `this.props.onTemperatureChange()` z nową wartością. Atrybuty tego komponentu, takie jak `onTemperatureChange`, pochodzą od rodzica - komponentu `Calculator`. * Podczas poprzedniego renderu, komponent `Calculator` określił, że atrybut `onTemperatureChange` przychodzący z komponentu `TemperatureInput` z Celsjuszami, jest wywoływany metodą `handleCelciusChange`, natomiast `onTemperatureChange` z komponentu z Fahrenheitami, jest teraz wywoływany metodą `handleFahrenheitChange`. Dzięki temu, w zależności od tego, który input dostanie nową wartość, jedna z tych metod zostanie wywołana. -* Wewnątrz tych metod, komponent `Calculator` prosi Reacta o ponowne renderowanie komponentu, poprzez użycie `this.setState()` z nową wartością inputa oraz obecnie używaną skalą. -* React wywołuje metodę `render` komponentu `Calculator`, by dowiedzieć się, jak powinien wyglądać jego UI. Wartości obu inputów są ponownie obliczane, bazując na aktualnych wartościach temperatury oraz aktywnej skali. To w tym miejscu odbywa się konwersja temperatury. -* React wywołuje metodę `render` indywidualnych komponentów `TemperatureInput` z ich nowymi atrybutami, które zostały określone przez komponent `Calculator`. React dowiaduje się wtedy jak powinien wyglądać UI komponentu. +* Wewnątrz tych metod komponent `Calculator` prosi Reacta o ponowne wyrenderowanie komponentu poprzez użycie `this.setState()` z nową wartością inputa oraz obecnie używaną skalą. +* React wywołuje metodę `render` komponentu `Calculator`, by dowiedzieć się, jak powinien wyglądać jego UI. Wartości obu inputów są ponownie obliczane, bazując na aktualnej temperaturze i skali. To w tym miejscu odbywa się konwersja temperatury. +* React wywołuje metodę `render` poszczególnych komponentów `TemperatureInput`, która otrzymuje nowe atrybuty od komponentu `Calculator`. Dzięki temu dowiaduje się, jak powinien wyglądać komponent. * React wywołuje metodę `render` komponentu `BoilingVerdict`, przekazując do niego temperaturę w Celsjuszach jako atrybut. -* DOM Reacta aktualizuje DOM rezultatem dopasowując się do wartości inputów. Input, który właśnie edytowaliśmy, otrzymuje swoją obecną wartość, natomiast drugi input otrzymuje temperaturę po konwersji. +* Paczka "React DOM" aktualizuje drzewo DOM otrzymaną strukturą, dopasowując się do wartości inputów. Input, którego właśnie edytowaliśmy, otrzymuje swoją obecną wartość, natomiast drugi input otrzymuje temperaturę po konwersji. Każda aktualizacja przechodzi ten sam proces, więc inputy są zawsze zsynchronizowane. ## Wnioski {#lessons-learned} -Wszelkie dane, które zmieniają się w aplikacji reactowej, powinny mieć swoje pojedyncze "źródło prawdy". Na ogół, stan dodaje się najpierw do komponentu, który potrzebuje go podczas renderowania. Następnie, jeśli inny komponent potrzebuje tych samych danych, możemy je "wynieść w górę" do najbliższego wspólnego przodka. Zamiast prób synchronizacji stanu pomiędzy różnymi komponentami, powinieneś polegać na [przepływie danych "z góry na dół"](/docs/state-and-lifecycle.html#the-data-flows-down). +Wszelkie dane, które zmieniają się w aplikacji reactowej, powinny mieć swoje pojedyncze "źródło prawdy". Na ogół stan dodaje się najpierw do komponentu, który potrzebuje go podczas renderowania. Następnie, jeśli inny komponent potrzebuje tych samych danych, możemy "wynieść je w górę" do najbliższego wspólnego przodka. Zamiast próbować synchronizować stan między różnymi komponentami, lepiej polegać na [przepływie danych "z góry na dół"](/docs/state-and-lifecycle.html#the-data-flows-down). -Wynoszenie stanu w górę wymaga pisanie bardziej "boilerplate'owo", niż wiązanie dwukierunkowe. Pozwala to jednak na łatwiejsze znalezienie i wyizolowanie błędów. Ponieważ każdy stan "żyje" w jakimś komponencie, a komponent ten może go zmienić, ryzyko powstania błędów jest znacznie mniejsze. Dodatkowo, można utworzyć dodatkową logikę, która odrzuci lub przeniesie dane wejściowe od użytkownika. +Wynoszenie stanu w górę skutkuje powstaniem większej ilości tzw. kodu szablonowego (ang. *boilerplate code*), niż wiązanie dwukierunkowe (ang. *two-way binding*), jednak pozwala na łatwiejsze znalezienie i wyizolowanie błędów. Ponieważ każdy stan "żyje" w jakimś komponencie i tylko ten komponent może go zmienić, ryzyko powstania błędów jest znacznie mniejsze. Ponadto, można dodać własną logikę, która pozwoli odrzucić lub przekształcić dane wejściowe od użytkownika. -Jeśli coś może być użyte zarówno z atrybutu, jak i stanu, prawdopodobnie nie powinno to pochodzić ze stanu. Na przykład, zamiast przetrzymywać zarówno `celciusValue` oraz `fahrenheitValue`, przechowujemy jedynie ostatnio edytowane `temperature` oraz `scale`. Wartość drugiego inputa może być przecież wyliczona podczas renderowania w metodzie `render()`. Pozwala nam to na edycję pozostałych pól, nie tracąc informacji przekazanej przez użytkownika. +Jeśli jakąś wartość można wydedukować na podstawie zarówno atrybutów, jak i stanu komponentu, to prawdopodobnie nie powinna się ona znaleźć w stanie. Na przykład, zamiast przetrzymywać jednocześnie `celciusValue` i `fahrenheitValue`, przechowujemy jedynie ostatnio edytowane `temperature` oraz `scale`. Wartość drugiego inputa może być przecież wyliczona w metodzie `render()`. Pozwala nam to na czyszczenie lub zaokrąglanie wartości w drugim polu bez utraty dokładności w danych pochodzących od użytkownika. -Jeśli zauważysz nieprawidłowości w UI swojej aplikacji, możesz użyć [Narzędzi Deweloperskich Reacta](https://github.com/facebook/react-devtools), by zbadać atrybuty i przemieszczać się po drzewie, dopóki nie znajdziesz komponentu odpowiedzialnego za zmianę stanu. Pozwoli Ci to znaleźć źródło błędów: +Jeśli zauważysz nieprawidłowości w interfejsie swojej aplikacji, możesz użyć [Narzędzi deweloperskich dla Reacta](https://github.com/facebook/react-devtools), aby zbadać atrybuty i przemieszczać się po drzewie, dopóki nie znajdziesz komponentu odpowiedzialnego za zmianę stanu. Pozwoli Ci to znaleźć przyczynę błędów: -Monitorowanie stanu w Narzędziach Deweloperskich Reacta +Monitorowanie stanu w narzędziach deweloperskich dla Reacta