Form

Elemen form HTML bekerja sedikit berbeda dari elemen DOM lainnya di React, karena elemen form secara natural menyimpan beberapa state internal. Sebagai contoh, form ini pada HTML biasa menerima nama tunggal:

<form>
  <label>
    Name:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="Submit" />
</form>

Form ini memiliki perilaku dasar dari form HTML biasa yakni menuju ke laman baru ketika user mengirim form tersebut. Jika Anda menginginkan perilaku seperti ini di React, ini sebenarnya dapat bekerja. Namun di banyak kasus, akan lebih mudah untuk memiliki sebuah fungsi JavaScript yang menangani sebuah submisi dari sebuah form dan memiliki akses terhadap data yang dimasukkan pengguna ke dalam form. Cara standar untuk mencapai hal ini adalah dengan teknik yang disebut ”controlled component“.

Controlled Component

Pada HTML, elemen form seperti <input>, <textarea>, dan <select> biasanya menyimpan state mereka sendiri dan memperbaruinya berdasarkan masukan dari pengguna. Di React, state yang dapat berubah seperti ini biasanya disimpan pada properti dari komponen, dan hanya akan diubah menggunakan setState().

Kita dapat menggabungkan keduanya dengan menggunakan state pada React sebagai “sumber kebenaran satu-satunya”. Kemudian komponen React yang me-render sebuah form juga mengontrol apa yang terjadi dalam form tersebut pada masukan pengguna selanjutnya. Sebuah elemen masukan form yang nilainya dikontrol oleh React melalui cara seperti ini disebut sebagai ”controlled component“.

Sebagai contoh, jika kita ingin membuat form pada contoh sebelumnya mencatat sebuah nama ketika nama dikirim, kita dapat menuliskan form sebagai sebuah controlled component:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

Coba di CodePen

Karena atribut value telah kita set pada elemen form, nilai yang ditampilkan akan selalu sama dengan this.state.value, yang menjadikan React sebagai sumber kebenaran tunggal dari state. Dan karena handleChange dijalankan setiap ketikan untuk memperbarui state React, nilai yang ditampilkan akan terbarui ketika pengguna mengetik.

Dengan sebuah controlled component, setiap perubahan state akan memiliki sebuah fungsi handler yang terkait. Hal ini memudahkan untuk memodifikasi atau memvalidasi masukan pengguna. Sebagai contoh, jika kita ingin mengharuskan nama untuk seluruhnya ditulis dengan huruf kapital, kita dapat menuliskan handleChange sebagai:

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});
}

Tag textarea

Pada HTML, elemen <textarea> mendefinisikan teks di dalamnya sebagai elemen anaknya:

<textarea>
  Hello there, this is some text in a text area
</textarea>

Di React, <textarea> menggunakan atribut value. Dengan cara ini, sebuah form yang menggunakan <textarea> dapat ditulis dengan cara yang sangat mirip dengan sebuah form yang menggunakan masukan satu baris:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

Perhatikan bahwa this.state.value diinisialisasi di constructor, sehingga area teks akan memiliki teks di dalamnya.

Tag select

Pada HTML, <select> membuat sebuah daftar drop-down. Sebagai contoh, HTML ini membuat sebuah daftar drop-down dari beberapa bumbu:

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

Perhatikan bahwa opsi Coconut mula-mula dipilih, karena adanya atribut selected. Di React, alih-alih menggunakan atribut selected, kita menggunakan atribut value di tag select. Hal ini lebih mudah dalam sebuah controlled component karena Anda hanya perlu mengubahnya di satu tempat saja. Sebagai contoh:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

Coba di CodePen

Secara keseluruhan, perubahan-perubahan ini membuat <input type="text">, <textarea>, dan <select> bekerja dengan cara yang mirip - mereka masing-masing menerima atribut value yang dapat Anda gunakan untuk mengimplementasikan controlled component.

Catatan

Anda bisa memasukan array ke atribut value, yang memungkinkan anda memilih beberapa opsi dalam tag select:

<select multiple={true} value={['B', 'C']}>

Masukkan Tag File

Dalam HTML, sebuah <input type="file"> membiarkan pengguna untuk memilih satu atau lebih berkas dari penyimpanan perangkat mereka untuk diunggah ke sebuah server atau dimanipulasi oleh JavaScript melalui Berkas API.

<input type="file" />

Karena nilai yang dimiliki adalah read-only, ini termasuk ke dalam uncontrolled component di React. Hal ini akan dibahas bersama dengan uncontrolled component lainnya selanjutnya di dokumentasi.

Menangani Banyak Input

Ketika Anda membutuhkan penanganan banyak elemen input terkontrol, Anda dapat menambahkan atribut name pada setiap elemen dan membiarkan fungsi handler memilih apa yang harus dilakukan berdasarkan nilai dari event.target.name.

Sebagai contoh:

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

Coba di CodePen

Perhatikan bagaimana kita meggunakan sintaks computed property name ES6 untuk mengubah state dengan key yang sesuai dengan nama dari masukan:

this.setState({
  [name]: value
});

Kode diatas setara dengan kode ES5 berikut:

var partialState = {};
partialState[name] = value;
this.setState(partialState);

Dan juga, karena setState() secara otomatis menggabungkan state parsial ke state yang sudah ada, kita hanya perlu memanggilnya dengan bagian yang berubah saja.

Mengendalikan Masukkan Nilai Kosong

Menentukan nilai prop pada controlled component mencegah pengguna mengubah masukan kecuali Anda menginginkannya. Jika Anda telah menetapkan nilai value namun masukan masih dapat diubah, Anda mungkin telah secara tidak sengaja menetapkan value ke undefined atau null.

Kode berikut menunjukkan contoh ini. (Mula-mula masukan terkunci tetapi menjadi dapat diubah setelah jeda singkat.)

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

Alternatif dari Controlled Components

Terkadang akan menjadi sulit untuk menggunakan controlled components, karena Anda perlu menulis event handler untuk setiap cara data Anda berubah dan menyalurkan semua masukan state melalui komponen React. Ini dapat menjadi sangat menjengkelkan ketika anda sedang mengkonversi basis kode yang sudah ada ke React, atau mengintegrasikan sebuah aplikasi React dengan sebuah library bukan-React. Pada situasi seperti ini, Anda mungkin ingin menggunakan uncontrolled components, sebuah teknik alternatif untuk mengimplementasikan masukan form.

Solusi Selengkapnya

Jika anda mencari solusi komplit termasuk validasi, melacak fields yang dikunjungi, dan menangani pengiriman form, Formik adalah salah satu pilihan populer. Namun, itu dibuat dengan prinsip yang sama dengan controlled components dan pengelolaan state — jadi jangan lalai untuk mempelajarinya.