diff --git a/content/docs/lifting-state-up.md b/content/docs/lifting-state-up.md index 449330cfd..55a9a2473 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: Memindahkan State ke Atas 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. +Seringkali, beberapa komponen perlu mencerminkan perubahan data yang sama. Kami menyarankan untuk memindah `state` yang digunakan bersama tersebut ke modul _parent_ terdekat yang dimiliki komponen-komponen terkait. Mari kita lihat bagaimana ini bekerja dalam aksi. -In this section, we will create a temperature calculator that calculates whether the water would boil at a given temperature. +Di bagian ini, kita akan membuat kalkulator suhu yang menghitung apakah air akan mendidih pada suhu tertentu. -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: +Kita akan memulai dengan komponen bernama `BoilingVerdict`. Komponen tersebut akan menerima suhu `celsius` sebagai *prop*, dan menentukan apakah suhu cukup untuk membuat air mendidih: ```js{3,5} function BoilingVerdict(props) { if (props.celsius >= 100) { - return
The water would boil.
; + returnAir akan mendidih.
; } - returnThe water would not boil.
; + returnAir tidak akan mendidih.
; } ``` -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`. +Selanjutnya, kita akan menampilkan komponen bernama `Calculator`. Komponen tersebut menampilkan `` yang memungkinkan Anda memasukkan suhu, dan menyimpan nilainya di `this.state.temperature`. -Additionally, it renders the `BoilingVerdict` for the current input value. +Selain itu, ini menampilkan `BoilingVerdict` untuk nilai masukan saat ini. ```js{5,9,13,17-21} class Calculator extends React.Component { @@ -56,13 +56,13 @@ class Calculator extends React.Component { } ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/ZXeOBm?editors=0010) +[**Coba di CodePen**](https://codepen.io/gaearon/pen/ZXeOBm?editors=0010) -## Adding a Second Input {#adding-a-second-input} +## Menambahkan Masukan Kedua {#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. +Syarat baru dari kita adalah selain masukan Celcius, kita juga memberikan masukan Fahrenheit, dan kedua masukan tersebut harus tetap sinkron. -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"`: +Kita dapat memulai dengan mengeluarkan komponen `TemperatureInput` dari `Calculator`. Kita akan menambahkan *prop* `scale` baru pada komponen tersebut yang dapat bernilai `"c"` atau `"f"`: ```js{1-4,19,22} const scaleNames = { @@ -86,7 +86,7 @@ class TemperatureInput extends React.Component { const scale = this.props.scale; return ( @@ -95,7 +95,7 @@ class TemperatureInput extends React.Component { } ``` -We can now change the `Calculator` to render two separate temperature inputs: +Kita sekarang dapat mengubah `Calculator` untuk membuat dua masukan suhu terpisah: ```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) +[**Coba di 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. +Kita memiliki dua masukan sekarang, tetapi ketika Anda memasukkan suhu di salah satunya, yang lain tidak diperbarui. Ini bertentangan dengan syarat kita: kita ingin tetap menyinkronkannya. -We also can't display the `BoilingVerdict` from `Calculator`. The `Calculator` doesn't know the current temperature because it is hidden inside the `TemperatureInput`. +Kita juga tidak dapat menampilkan `BoilingVerdict` dari `Calculator`. `Calculator` tidak tahu suhu saat ini karena suhu tersebut tersembunyi di dalam `TemperatureInput`. -## Writing Conversion Functions {#writing-conversion-functions} +## Menulis Fungsi Konversi {#writing-conversion-functions} -First, we will write two functions to convert from Celsius to Fahrenheit and back: +Pertama, kita akan menulis dua fungsi untuk mengkonversi suhu dari Celsius ke Fahrenheit dan sebaliknya: ```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. +Dua fungsi ini akan mengkonversi angka. Kita akan menulis fungsi lain yang menggunakan fungsi *string* `temperature` dan fungsi konverter sebagai argumen dan mengembalikan string. Kita akan menggunakannya untuk menghitung nilai dari suatu masukan bersarakan masukan lainnya. -It returns an empty string on an invalid `temperature`, and it keeps the output rounded to the third decimal place: +Ini mengembalikan *string* kosong pada `temperature` yang tidak valid, dan itu membuat keluaran dibulatkan ke tempat desimal ketiga: ```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'`. +Misalnya, `tryConvert('abc', toCelsius)` menghasilkan *string* kosong, dan `tryConvert('10.22', toFahrenheit)` menghasilkan `'50.396'`. -## Lifting State Up {#lifting-state-up} +## Memindahkan State ke Atas {#lifting-state-up} -Currently, both `TemperatureInput` components independently keep their values in the local state: +Saat ini, kedua komponen `TemperatureInput` secara independen menyimpan nilainya di `state` lokal: ```js{5,9,13} class TemperatureInput extends React.Component { @@ -166,18 +166,18 @@ class TemperatureInput extends React.Component { render() { const temperature = this.state.temperature; - // ... + // ... ``` -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. +Namun, kita ingin kedua masukan selaras satu sama lain. Ketika kita memperbarui masukan Celcius, masukan Fahrenheit harus mencerminkan suhu yang dikonversi, dan sebaliknya. -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. +Dalam React, *state* bersama dicapai dengan memindahkannya ke komponen induk bersama terdekat dari komponen yang membutuhkannya. Ini disebut "memindahkan *state* ke atas". Kita akan menghapus *state* lokal dari `TemperatureInput` dan memindahkannya ke `Calculator` sebagai gantinya. -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. +Jika `Calculator` memiliki *state* bersama, *state* tersebut akan menjadi "sumber kebenaran" untuk suhu saat ini di kedua masukan. Ini dapat menginstruksikan kedua input untuk memiliki nilai yang konsisten satu sama lain. Karena kedua `props` pada komponen `TemperatureInput` berasal dari komponen `Calculator` induk yang sama, kedua masukan akan selalu sinkron. -Let's see how this works step by step. +Mari kita lihat bagaimana ini bekerja selangkah demi selangkah. -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: +Pertama, kita akan ganti `this.state.temperature` dengan `this.props.temperature` di komponen `TemperatureInput`. Untuk saat ini, mari kita berpura-pura `this.props.temperature` sudah ada, meskipun kita harus menampilkannya dari `Calculator`: ```js{3} render() { @@ -186,11 +186,11 @@ First, we will replace `this.state.temperature` with `this.props.temperature` in // ... ``` -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. +Kita tahu bahwa [*props* bersifat *read-only*](/docs/components-and-props.html#props-are-read-only). Ketika `temperature` berada di *state* lokal, `TemperatureInput` bisa memanggil `this.setState()` untuk mengubahnya. Namun, karena sekarang `temperature` berasal dari induknya sebagai sebuah `prop`, `TemperatureInput` tidak memiliki kendali atasnya. -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`. +Dalam React, ini biasanya diselesaikan dengan membuat komponen "dikendalikan". Sama seperti *DOM* `` menerima kedua `value` dan `onChange` *prop*, begitu juga dapat menyesuaikan `TemperatureInput` menerima kedua `temperature` dan `onTemperatureChange` *props* dari `Calculator` sebagai induknya. -Now, when the `TemperatureInput` wants to update its temperature, it calls `this.props.onTemperatureChange`: +Sekarang, ketika `TemperatureInput` ingin memperbarui suhunya, ia memanggil `this.props.onTemperatureChange`: ```js{3} handleChange(e) { @@ -199,13 +199,13 @@ Now, when the `TemperatureInput` wants to update its temperature, it calls `this // ... ``` ->Note: +>Catatan: > ->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. +>Tidak ada arti khusus untuk salah satu `temperature` atau `onTemperatureChange` nama *prop* dalam komponen khusus. Kita bisa memanggil mereka apapun, seperti memberi nama mereka `value` dan `onChange` yang merupakan konvensi umum. -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. +*Prop* `onTemperatureChange` akan diberikan bersama-sama dengan *prop* `temperature` oleh komponen induk `Calculator`. Fungsi ini akan menangani perubahan dengan memodifikasi *state* lokalnya sendiri, sehingga menampilkan ulang kedua masukan dengan nilai-nilai baru. Kita akan segera melihat implementasi `Calculator` yang baru. -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`: +Sebelum melihat perubahan di `Calculator`, mari kita rekap perubahan kita ke komponen `TemperatureInput`. Kita telah menghapus *state* lokal darinya, dan alih-alih membaca `this.state.temperature`, kita sekarang membaca `this.props.temperature`. Alih-alih memanggil `this.setState()` saat kita ingin melakukan perubahan, kita sekarang memanggil `this.props.onTemperatureChange()`, yang akan disediakan oleh `Calculator`: ```js{8,12} class TemperatureInput extends React.Component { @@ -223,7 +223,7 @@ class TemperatureInput extends React.Component { const scale = this.props.scale; return ( @@ -232,11 +232,11 @@ class TemperatureInput extends React.Component { } ``` -Now let's turn to the `Calculator` component. +Sekarang mari kita beralih ke komponen `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. +Kita akan menyimpan masukan `temperature` saat ini dan `scale` dalam *state* lokal. Ini adalah kondisi yang kita "angkat" dari masukan, dan itu akan berfungsi sebagai "sumber kebenaran" untuk keduanya. Ini adalah representasi minimal dari semua data yang perlu kita ketahui untuk membuat kedua masukan. -For example, if we enter 37 into the Celsius input, the state of the `Calculator` component will be: +Misalnya, jika kita memasukkan 37 ke dalam masukan Celcius, *state* pada komponen `Calculator` akan menjadi: ```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: +Jika nanti kita mengubah kolom Fahrenheit menjadi 212, state pada `Calculator` akan menjadi: ```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. +Kita bisa menyimpan nilai dari kedua masukan tetapi tidak perlu. Cukup untuk menyimpan nilai masukan yang terakhir diubuah, dan skala yang diwakilinya. Kita kemudian dapat menyimpulkan nilai masukan lain berdasarkan alur `temperature` dan `scale` sendiri. -The inputs stay in sync because their values are computed from the same state: +Masukan tetap sinkron karena nilainya dihitung dari *state* yang sama: ```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) +[**Coba di 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. +Sekarang, tidak peduli apabila masukan berubah, `this.state.temperature` dan `this.state.scale` dalam `Calculator` akan diperbarui. Salah satu masukan mendapatkan nilai apa adanya, sehingga setiap masukan pengguna dipertahankan, dan nilai masukan lainnya selalu dihitung ulang berdasarkan masukan. -Let's recap what happens when you edit an input: +Mari kita rekap apa yang terjadi ketika Anda mengubah masukan: -* 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 memanggil fungsi yang disebut `onChange` pada DOM ``. Dalam kasus kita, ini adalah metode `handleChange` dalam komponen `TemperatureInput`. +* Metode `handleChange` dalam komponen `TemperatureInput` memanggil `this.props.onTemperatureChange()` dengan nilai baru. Itu merupakan *props*, termasuk `onTemperatureChange`, disediakan oleh komponen induknya, `Calculator`. +* Ketika sebelumnya di-*render*, `Calculator` telah menentukan bahwa `onTemperatureChange` dari `TemperatureInput` Celcius adalah metode `handleCelsiusChange` milik `Calculator`, dan `onTemperatureChange` dari `TemperatureInput` Fahrenheit adalah metode `handleFahrenheitChange` milik `Calculator`. Jadi salah satu dari dua metode `Calculator` dipanggil tergantung pada masukan yang kita ubah. +* Di dalam metode ini, komponen `Calculator` meminta React untuk mengulang *render* dengan memanggil `this.setState()` dengan nilai masukan baru dan skala masukan saat ini yang baru saja kita ubah. +* React memanggil komponen `Calculator` metode `render` untuk mempelajari seperti apa tampilan antarmuka pengguna. Nilai kedua masukan dihitung berdasarkan suhu saat ini dan skala aktif. Konversi suhu dilakukan di sini. +* React memanggil metode `render` pada komponen `TemperatureInput` individual dengan *props* baru yang ditentukan oleh `Calculator`. Mreka kemduain mempelajari seperti apa tampilan antarmuka pengguna mereka. +* React memanggil metode `render` pada komponen `BoilingVerdict`, melewati suhu dalam Celcius sebagai *props*. +* React DOM memperbarui dengan vonis didih dan untuk mencocokan nilai masukan yang diinginkan. Masukan yang baru saja kita ubah menerima nilainya saat ini, dan masukan lainnya diperbarui ke suhu setelah konversi. -Every update goes through the same steps so the inputs stay in sync. +Setiap pembaruan melewati langkah yang sama sehingga masukan tetap sinkron. -## Lessons Learned {#lessons-learned} +## Pelajaran yang Dipetik {#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). +Seharusnya ada "sumber kebenaran" tunggal untuk setiap data yang berubah dalam aplikasi React. Biasanya, *state* ditambahkan pertama kali ke komponen yang membutuhkannya untuk di-*render*. Kemudian, jika komponen lain juga membutuhkannya, Anda dapat memindahkan *state* ke komponen induk bersama terdekat. Alih-alih mencoba menyinkronkan *state* antara komponen yang berbeda, Anda harus mengandalkan [aliran data dari atas kebawah](/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. +Pengangkatan *state* melibatkan penulisan lebih banyak kode "*boilerplate*" daripada pendekatan pengikatan dua arah, tetapi sebagai manfaat, dibutuhkan lebih sedikit pekerjaan untuk menemukan dan mengisolasi *bug*. Karena tiap *state* akan "hidup" di sebuah komponen dan hanya komponen itu sendiri yang dapat mengubahnya, area permukaan untuk *bug* akan sangat berkurang. Selain itu, Anda dapat menerapkan logika khusus apa pun untuk menolak atau mengubah masukan pengguna. -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. +Jika sesuatu dapat diturunkan dari *props* atau *state*, hal itu mungkin tidak sebaiknya berada di *state*. Sebagai contoh, alih-alih menyimpan keduanya `celsiusValue` dan `fahrenheitValue`, kita menyimpan hanya `temperature` yang terakhir diubah dan `scale`-nya. Nilai dari masukan lain selalu dapat dihitung dari kedua nilai tersebut dalam metode `render()`. Ini memungkinkan kita menghapus atau menerapkan pembulatan ke masukan lain tanpa kehilangan ketepatan pada masukan pengguna. -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: +Ketika Anda melihat sesuatu yang salah di antarmuka pengguna, Anda dapat menggunakan [Alat Pengembang React](https://github.com/facebook/react-devtools) untuk memeriksa *props* dan menelusuri *tree* komponen Anda ke atas sampai Anda menemukan komponen yang bertanggung jawab untuk memperbarui *state*. Ini memungkinkan Anda melacak *bug* ke sumbernya: