Less talk, more code

The blog of Iskandar Soesman

React Untuk Pemula

Tulisan ini ditujukan terutama untuk saya sendiri yang baru mau belajar tentang React dan teman-teman yang juga tertarik untuk mengenal dan mempelajari React.

Untuk bisa mengikuti tutorial ini setidaknya anda harus sudah memahami dasar-dasar javascript terutama ECMAScript 6 (ES6). Anda juga diharapkan sudah menginstall Nodejs dan NPM pada komputer yang anda gunakan.

Apa itu React?

React adalah sebuah javascript library untuk mempermudah dalam membuat user interface. Jika kita pernah mendengar terminologi MVC, maka bisa dikatakan React adalah bagian dari V, yaitu view di terminologi ini. Jadi React dibuat khsus untuk mempermudah pengelolaan view.

Mari kita lihat contoh html di bawah ini.

<header>
    <div id="name">
        Not Logged In
    </div>
</header>

Jika kita menggunakan jQuery untuk handle login secara dinamis, maka code kita akan seperti:

$.post('/auth', {username:'foo', password:'bar'}, function( user ) {
    // modifikasi dom dengan id name
    $('#name').text( user.name );
});

Dari contoh di atas kita akan menemukan beberapa issue.

  • Siapakah yang melakukan update pada tag header?
  • Siapa saja yang punya akses untuk memodifikasi HTML header ini?
  • Siapa yang bertanggung jawab penuh untuk merubah nilai #name?

Jika code kita masih sederhana, mungkin masih mudah untuk mengidentifikasi, namun jika code kita sudah besar, apa lagi ada banyak developer yang terlibat, maka identifikasi akan semakin sulit. Persoalan lainnya adalah setiap orang punya akses untuk memodifikasi setiap dom, sehingga pada contoh di atas kita bisa saja punya banyak controller yang memodifikasi tag header kita.

Sekarang mari kita lihat bagaimana React mengatasi masalah ini dengan pendekatan component.

var Header = React.createClass({
    getInitialState: function() {
        return {name:'Not Logged In'};
    },
    componentDidMount: function() {
        $.post('/auth', {username:'foo', password:'bar'}, function( user ) {
            this.setState({name: user.name});
        });
    },
    render: function() {
        return (<header>{ this.state.name }</header>);
    }
});

ReactDOM.render(
  <Header />,
  document.getElementById('content')
);

Pada react, setiap bagian dari view diwakili oleh sebuah component. Dalam contoh di atas komponen kita bernama Header. Pendekatan ini menjamin bahwa setiap component bertanggungjawab hanya pada satu bagian dari view kita. Di React, sebuah component biasanya dibungkus ke dalam sebuah class.

Setup Project

Buat sebuah folder reactproject atau dengan nama lain pilihan anda kemudian buat folder src dan public di dalamnnya.

cd reactproject
mkdir src public
touch src/main.js public/index.html
npm init

Isikan informasi nama project dan deskripsinya.

npm install --save react react-dom

Kemudian install package yang dibutuhkan selama proses development

npm install --save-dev babel-preset-react babel-loader babel-core babel-preset-es2015

Buat sebuah file .babelrc di root folder.

vim .babelrc

Kemudian isikan dengan nilai

{ "presets": ["react"] }

Buat sebuah file webpack.config.js kemudian isikan

module.exports = {
  entry: {
    app: ["./src/main.js"]
  },
  output: {
    path: "./public/assets/",
    publicPath: "/assets/",
    filename: "bundle.js"
  }
};

Selama proses development, kita membutuhkan webserver untuk pengetesan. Untuk mempercepat proses, kita bisa menggunakan Webpack Dev Server. Webpack Dev Server ini juga sudah mendukung autoreload, sehingga kita tidak perlu lagi me-refrash browser pada saat selesai melakukan edit pada file main.js.

Install Webpack Dev Server

npm install  --save-dev webpack-dev-server

Isikan file public/index.html dengan:

<!DOCTYPE html>
<html>
  <head>
    <title>React Project</title>
  </head>
  <body>
    <div id="content"></div>
    <script src="/assets/bundle.js"></script>
  </body>
</html>

Isikan file src/main.js dengan:

document.write("FooBar");

Setelah itu jalankan perintah:

webpack

Lalu jalankan web server development:

webpack-dev-server --content-base public/

Kemudian buka browser http://localhost:8080/

Untuk mengaktifkan mode autoreload, stop web server kemudian jalankan perintah:

webpack-dev-server --content-base public/ --progress --inline

--progress adalah untuk menunjukan progess pada saat proses kompilasi berlangsung dan --inline untuk automatic refresh browser.

Silahkan dirubah-rubah isi file src/main.js kemudian langsung lihat perubahannya di browser.

Babel dan React

Kita sebelumnya telah menginstall babel. Babel adalah JavaScript compiler untuk merubah syntax ES2015 menjadi native javascript. Ada beberapa konfigurasi untuk bisa menjalankan bable. Tambahkan konfigurasi berikut di file webpack.config.js

module.exports = {
  entry: {
    app: ["./src/main.js"]
  },
  output: {
    path: "./public/assets/",
    publicPath: "/assets/",
    filename: "bundle.js"
  },
  module: {
    loaders: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel",
      include: __dirname,
      query: {
        presets: [ 'es2015', 'react' ]
      }
    }]
  }
};

Loaders digunakan sebagai preprocessors seperti yang dijelaskan pada halaman dokumentasi webpack

Loaders are kind of like "tasks" are in other build tools, and provide a powerful way to handle frontend build steps. Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs.

Untuk mengaktifkan konfigurasi tambahan ini silahkan restart webserver.

Setelah itu silahkan edit file src/main.js dengan syntax ES6 seperti contoh berikut

let docBody = "Hallo ES6";
document.write(docBody);

Mari kita buat sebuah class React. Edit file src/main.js dan isikan dengan:

import React from "react";
import { render } from "react-dom";

class MyApp extends React.Component {
  constructor() {
    super();
    this.state = { title: "Hello React!" };
  }

  render() {
    return (
        <h1>{this.state.title}</h1>
    );
  }
}

const App = MyApp;

render(
  <App/>,
  document.getElementById('content')
);

Kemudian lihat hasilnya pada browser.

Membuat Component

Seperti yang sudah dijelaskan sebelumnya, bahwa setiap bagian pada interface akan dipecah-pecah menjadi component. Untuk itu selanjutnya kita memerlukan beberapa folder dan file tambahan.

Buat sebuah folder baru di dalam src dengan nama components dan buat sebuah file App.js

mkdir src/components
touch src/components/App.js

Kita pindahkan beberapa bagian isi file src/main.js ke src/components/App.js

import React from "react";

export default class MyApp extends React.Component {
  constructor() {
    super();
    this.state = { title: "Hello React!" };
  }

  render() {
    return (
        <h1>{this.state.title}</h1>
    );
  }
}

Kemudian kita rubah file main.js menjadi:

import React from "react";
import { render } from "react-dom";
import App from "./components/App";

render(
  <App/>,
  document.getElementById('content')
);

Konvensi Penamaan di React

Ketika membuat sebuah komponen di React, kita menggunakan JSX syntax untuk menambahkan tag html di dalam code javascript. JSX adalah sebuah compiler yang memungkinkan kita menulis tag html di dalam javascrpt syntax.

Untuk membedakan tag html dengan komponen dari React pada JSX, maka ada beberapa konvensi penamaan yang harus diikuti. Yaitu:

  • Nama natif element HTML dimulai dengan huruf kecil contoh <head>, <div>, <header>
  • Nama sebuah class React dimulai dengan huruf kapital contoh <App>, <Header>
  • Event handlers pada sebuah komponen menggunakan gaya penulisan camelCase contoh onClick

Tentang Props dan State

Di React, kita mempunyai dua "model" input data yang membantu dalam pengolahan dan manipulasi data, yaitu props dan state.

Props (kependekaan dari properties), adalah nilai awal dari sebuah component. Bisa juga disebut nilai opsi dari komponen. Sifatnya adalah immutable dan deklarasinya dilakukan pada saat sebuah component baru akan dipanggil.

State adalah sebuah nilai default dari sebuah component ketika pertama kali di-mount. Sebuah state sfatnya unimmutable private yang ini berarti hanya bisa dimodifikasi dari dalam class component itu sendiri.

Berikut table yang membedakan antara props dan state yang diambil dari github.com/uberVU/react-guide

Feature props state
Bisa mendapatkan nilai awal dari parent Component? Yes Yes
Bisa dirubah oleh parent Component? Yes No
Bisa men-set nilai default di dalam Component? Yes Yes
Nilainya bisa berubah di dalam Component? No Yes
Bisa men-set nilai awal untuk child Components? Yes Yes
Nilainya bisa berubah di child Components? Yes No

Mari kita modifikasi code yang sudah kita tulis sebelumnya. Pertama kita edit file main.js menjadi seperti berikut:

import React from "react";
import { render } from "react-dom";
import App from "./components/App";

render(
  <App name="ReactJS"/>,
  document.getElementById('content')
);

Kemudian kita edit file components/App.js menjadi berikut:

import React from "react";

export default class MyApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = { title: this.props.name };
  }

  render() {
    return (
        <h1>Hello {this.state.title}</h1>
    );
  }
}

Pada contoh diatas kita mendapatkan nilai name ketika component App pertamakali di mount <App name="ReactJS"/>. selanjutnya cara mendapatkan nilai name di dalam component adalah dengan menggunakan variable this.props.name.

Pada method constructor kita menyimpan nilai dari props name ke dalam state title dan cara mendapatkan nilai dari title ini adalah dengan variable this.state.title.

Memanggil Sebuah Component dari Component Lain

Mari kita buat sebuah component baru dengan nama HelloMessage. Buat sebuah file baru src/components/HelloMessage.js kemudian isikan dengan:

import React from "react";

export default class HelloMessage extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <h1>Hello {this.props.myname}</h1>
        );
    }
}

Edit file components/App.js menjadi seperti berikut:

import React from "react";
import HelloMessage from "./HelloMessage";

export default class MyApp extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
        <header style={{backgroundColor:'gray'}}>
            <HelloMessage myname="World" />
        </header>
    );
  }
}

Untuk merubah nilai state React telah menyediakan API this.setState().

Lihat hasilnya pada browser.

Event handlers

Pada contoh selanjutnya kita akan merubah warna backgroundColor pada component App. Selanjutnya update code app.js menjadi seperti berikut:

import React from "react";
import HelloMessage from "./HelloMessage";

export default class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = { color: "gray" };
    }

    handleClick(e) {
        let backgroundColor = this.state.color == "gray" ? "red":"gray";
        this.setState({ color: backgroundColor });

        e.preventDefault();
    }

    render() {
        return (
            <header onClick={this.handleClick.bind(this)} style={{backgroundColor:this.state.color}}>
            <HelloMessage myname="World" />
            </header>
        );
    }
}

Silahkan clik pada bagian header di browser anda.

Ubah Nilai state Pada Component Parent

Kali ini kita akan merubah nilai state pada Component parent yang di-trigger dari Component child. Ubah file app.js menjadi seperti berikut:

import React from "react";
import HelloMessage from "./HelloMessage";

export default class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = { color: "gray" };
    }

    handleClick(e) {
        let backgroundColor = this.state.color == "gray" ? "red":"gray";
        this.setState({ color: backgroundColor });

        e.preventDefault();
    }

    render() {
        return (
            <header style={{backgroundColor:this.state.color}}>
            <HelloMessage clickHandler={this.handleClick.bind(this)} myname="World" />
            </header>
        );
    }
}

Kemudian rubah file HelloMessage.js menjadi seperti berikut:

import React from "react";

export default class HelloMessage extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <h1 onClick={this.props.clickHandler}>Hello {this.props.myname}</h1>
        );
    }
}

Silahkan coba lagi untuk clik di bagian "Hello World".

Ubah Nilai state Pada Component Sibling

Kali ini kita akan merubah nilai state sebuah component yang sumber nilainya dari component lain yang selevel.

Buat sebuah component beru dengan nama TextInput kemudian isi dengan code berikut:

import React from "react";

export default class TextInput extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            myInput: ""
        };
    }

    handleType(e) {
        this.setState({myInput: e.target.value});
    }

    handleSubmit(e) {
        this.props.parentListener(this.state.myInput);
        this.setState({myInput: ""});
        e.preventDefault();
    }

    render() {
        return (
            <form action="#" onSubmit={this.handleSubmit.bind(this)}>
                <input onChange={this.handleType.bind(this)} type="text" value={this.state.myInput}></input>
            </form>
        );
    }
}

Kemudian edit file app.js menjadi berikut:

import React from "react";
import HelloMessage from "./HelloMessage";
import TextInput from "./TextInput";

export default class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            color: "gray",
            key: Math.random(),
            myName: "World"
        };
    }

    handleClick(e) {
        let backgroundColor = this.state.color == "gray" ? "red":"gray";
        this.setState({ color: backgroundColor });

        e.preventDefault();
    }

    handleInputFromChild(val) {
        this.setState({
            key: Math.random(),
            myName:val
        });
    }

    render() {
        return (
            <section>
                <header style={{backgroundColor:this.state.color}}>
                    <HelloMessage key={this.state.key} clickHandler={this.handleClick.bind(this)} myname={this.state.myName} />
                </header>
                <TextInput parentListener={this.handleInputFromChild.bind(this)}/>
            </section>
        );
    }
}

Silahkan testing dengan mengisikan nama apapun pada input text di bawah header dan kemudian tekan enter.

Sampai pada tahap ini kita sudah mempelajari konsep-konsep dasar dari React. Untuk selanjutnya kita akan membuat aplikasi Twitter sederhana dengan React.