5 Contoh Aplikasi Sederhana untuk Belajar Vue.js

Kamu pasti sudah sering mendengar tentang framework javascript yang di buat oleh Facebook ini, yaitu React. React di gunakan oleh banyak website populer, termasuk Facebook sendiri dan juga Instagram. Di artikel kali ini ada 5 contoh aplikasi yang dibuat menggunakan React, yang semoga bisa membantu kamu dalam mempelajari React.

Apa yang spesial dari React?

Konsep utama dalam pembuatan React berpusat pada komponen. Berbeda dengan framework lain seperti Angular dan Ember, yang menggunakan two-way data binding untuk memperbarui HTML. React lebih kecil dan juga lebih mudah digunakan terutama mudah dikombinasikan dengan JQuery maupun library Javascript lainnya. React juga sangat cepat karena menggunakan virtual DOM, dan hanya memperbarui bagian HTML yang berubah.

Tetapi kekurangannya kita perlu mengetik lebih banyak kode untuk membuat hal yang sama yang bisa dengan mudah dibuat bila dengan data binding, seperti pada contoh-contoh aplikasi dibawah ini. Untuk perbanding baca juga artikel contoh aplikasi menggunakan Vue.js.

Bagaimana memulainya?

Untuk mempermudah setup kita menggunakan create-react-app yang cara pakainya sudah di jelaskan di Memulai Belajar React JS.

1. Timer

Seperti yang sudah di sebutkan sebelumnya React itu berfokus pada komponen. Yang dibuat dengan sebuah class yang meng-extends React.Component. Setiap komponen memiliki state (Object berisikan data) dan bertanggung jawab dalam me-render dirinya sendiri. Fungsi render() dipanggil ketika ada perubahan pada state. Berikut adalah contoh untuk membuat timer sederhana:

index.html

<div id="root"></div>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App start={Date.now()}/>, document.getElementById('root'));

App.js

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

    // Ini adalah state awal yang di set sebelum render
    this.state = {
      elapsed: 0,
    };

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

  componentDidMount() {

    // componentDidMount dipanggil setelah component di render ke DOM.
    // Kita bisa set interval disini

    this.timer = setInterval(this.tick, 50);
  }

  componentWillUnmount() {

    // Method componentWillUnmount dipanggil sesaat sebelum
    // component di buang dari halaman
    // Kita bisa clear interval disini.

    clearInterval(this.timer);
  }

  tick() {

    // Fungsi ini dipanggil setial 50ms
    // Kita update elapsed disini.
    // Pemanggilan setState memicu component untuk melakukan re-render

    this.setState({
      elapsed: new Date() - this.props.start,
    })
  }

  render() {
    const elapsed = Math.round(this.state.elapsed / 100);

    const seconds = (elapsed / 10).toFixed(1);


    return <p>Contoh ini sudah dimulai sejak <b>{seconds} seconds</b> yang lalu</p>;
  }
}

DEMO Timer

Kita bisa memasukkan ekspresi javascript apapun di dalam kurung kurawal {} ketika membuat komponen. Di atas kita memasukkan Date.now() yang akan dipakai oleh fungsi tick() untuk menghitung jumlah detik yang telah berlalu.

2. Navigation Menu

Pada contoh berikut kita lihat bagaimana kita meng-handle event klik dengan menggunakan React:

index.html

<div id="root"></div>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

const items = ['Home', 'Services', 'About', 'Contact us'];

ReactDOM.render(<App items={items}/>, document.getElementById('root'));

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      active: 'Home',
    };
  }

  clicked(menu) {

    // Fungsi ini akan memperbarui state dengan
    // dengan nilai menu yang dipilih
    this.setState({
      active: menu,
    });
  }

  render() {
    return (
      <div id="app">

          {/* map akan loop sebanyak menu yang telah kita definisikan */}
          {/* dan kemudian mengembalikan elemen baru yaitu <a/> */}
          <nav className="nav">{ this.props.items.map((menu, index) => {
            var style = 'menu';

            if (this.state.active === menu) {
              style = `${style} is-active`;
            }

            return <a
                    className={style}

                    // Kita menggunakan bind sehingga 'menu' bisa di kirim ke
                    // fungsi 'clicked'
                    onClick={this.clicked.bind(this, menu)}
                    key={index}
                  >
                    {menu}
                  </a>;
          }) }
          </nav>

          <div className="info">
            Anda memilih <span className="selected">{this.state.active}</span>
          </div>
      </div>
    );
  }
}

DEMO Navigation Menu

Kamu mungkin heran dengan atribut className yang tidak ada di HTML. Ini karena ketika kita menulis <a />, React tidak mengembalikan element, namun:

React.createElement(
  'a',
  {className: 'is-active'},
  null
)

Selengkapnya bisa kamu baca di sini.

Instant Search

Contoh yang ini tentang bagaimana kita bisa membuat Instant Search dengan React. Kita juga punya contoh serupa dengan menggunakan Vue.js.

index.html

<div id="root"></div>

index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';

    const products = [
    {
        name: 'Cappucino',
        active: true,
    }, {
        name: 'Green Tea Latte',
        active: true
    },
    {
        name: 'Fish and Chips',
        active: true,
    },
    {
        name: 'Tuna Sandwich',
        active: true,
    },
    {
        name: 'Mineral Water',
        active: false,
    },
    {
        name: 'French Fries',
        active: false,
    },
    ];

    ReactDOM.render(<App products={products} />, document.getElementById('root'));

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      searchTerms: '',
    };

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

  handleChange(e) {

    // Jika this.setState di bawah itu di comment, nilai text box tidak akan berubah
    // Karena, di React input tidak dapat merubah valuenya secara langsung.

    this.setState({
      searchTerms: e.target.value,
    });
  }

  render() {
    const { products } = this.props;
    const searchTerms = this.state.searchTerms.trim().toLowerCase();
    let filteredProducts = products;

    // Pencarian dengan menggunakan filter

    if (searchTerms.length > 0) {
      filteredProducts = products.filter((product) => {
        return product.name.toLowerCase().indexOf(searchTerms) !== -1;
      });
    }

    return (
      <form id="app">
        <div className="search">
          <input
            type="text"
            placeholder="Masukkan kata kunci pencarian"
            onChange={this.handleChange}
          />
        </div>

        <ul>
          {filteredProducts.map((product, index) => {
            return (
              <li key={index}>
                <p>{product.name}</p>
              </li>
            )
          })}
        </ul>
      </form>
    );
  }
}

DEMO Instant Search

Pada contoh diatas kita juga mendemonstrasikan konsep penting lain di React. Yaitu, perubahan pada state seharusnya juga merubah HTML, bukan sebaliknya. Ketika value text box di set, isinya akan tetap itu, mengabaikan input dari user, kecuali kita update menggunakan fungsi onChange.

4. Order Form

Keunggulan React terasa terutama ketika kita mengkombinasikan banyak komponen. Dengan begitu kita bisa membuat struktur kode yang lebih baik. Ini juga membuat kode kita bisa digunakan berulang kali di berbagai bagian lain aplikasi (reusable). Berikut contoh berupa pesana serta total yang harus dibayar:

index.html

<div id="root"></div>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const products = [
  {
    name: 'Cappucino',
    price: 35000,
    active: true,
  },
  {
    name: 'Green Tea Latte',
    price: 40000,
    active: true
  },
  {
    name: 'Fish and Chips',
    price: 50000,
    active: true,
  },
  {
    name: 'Tuna Sandwich',
    price: 45000,
    active: true,
  },
  {
    name: 'Mineral Water',
    price: 8000,
    active: false,
  },
  {
    name: 'Frence Fries',
    price: 18000,
    active: false,
  },
];

ReactDOM.render(<App products={products} />, document.getElementById('root'));

Product.js

import React, { Component } from 'react';

class Product extends Component {
  constructor(props) {
    super(props);

    this.state = {
      active: false,
    };

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

  onClick(e) {
    const { price } = this.props.product;
    const active = !this.state.active;

    this.setState({
      active: active,
    });

    // Beritahu Komponen OrderForm untuk mengupdate total
    this.props.addTotal( active ? price : -price );
  }

  render() {
    const { name, price } = this.props.product;
    const { active } = this.state;

    return (
      <li className={active ? 'is-active' : ''} onClick={this.onClick}>
        {name}
        <span>Rp { price }</span>
      </li>
    );
  }
}

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Product from './Product';

class OrderForm extends Component {
  constructor(props) {
    super(props);

    this.state = {
      total: 0,
    };

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

  addTotal(price) {
    this.setState({
      total: this.state.total + price,
    });
  }

  render() {
    const services = this.props.products.map((product, index) => {

      // Buat component Product untuk setiap product di dalam Array Products
      // Perhatikan kita juga mengirim fungsi addTotal
      return <Product product={product} addTotal={this.addTotal} key={index}/>;
    })

    return (
      <form id="app">
        <h1>Pesanan</h1>
        <ul>
          {services}
        </ul>

        <div className="total">
          Total:
          <span>Rp {this.state.total}</span>
        </div>
      </form>
    );
  }
}

DEMO Order Form

Masalah pertama yang biasa kita hadapi ketika mengkombinasikan banyak komponen adalah bagaimana membuat komunikasi antar komponen. Cara pertama adalah dengan memasukkan data yang dibutuhkan di dalam atribut ketika kita menggunakan komponen. Tetapi ini hanya untuk komunikasi dari parent component ke child component. Untuk komunikasi dari child ke parent kita bisa mengirim fungsi parent di atribut child. Kemudian child memanggil fungsi tersebut, sehingga parent mendapat data yang di kirim melalui fungsi tersebut. Baca lebih tentang komunikasi antar komponen React di sini

Switchable Grids

Contoh berikutnya sering muncul di dunia nyata ketika aplikasi memiliki lebih dari satu layout tergantung dari suatu kondisi. Dengan menekan tombol yang ada di atas aplikasi, kita bisa merubah model layout aplikasi. Antara grid, yang menampilkan gambar, dengan list yang menampilkan gambar dan nama produk.

Di dalam contoh ini juga ada lebih banyak komponen, jadi sekalian juga untuk contoh bagaimana komunikasi antar komponen React dapat dilakukan.

index.html

<div id="root"></div>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

const products = [
  {
    name: 'Cappucino',
    image: {
        medium: 'img/cappucino-300x300.jpg',
        small: 'img/cappucino-150x150.jpg',
    }
  },
  {
    name: 'Green Tea Latte',
    image: {
        medium: 'img/green-tea-latte-300x300.jpg',
        small: 'img/green-tea-latte-150x150.jpg',
    }
  },
  {
    name: 'Fish and Chips',
    image: {
        medium: 'img/fish-and-chips-300x300.jpg',
        small: 'img/fish-and-chips-150x150.jpg',
    }
  },
  {
    name: 'Tuna Sandwich',
    image: {
        medium: 'img/tuna-sandwich-300x300.jpg',
        small: 'img/tuna-sandwich-150x150.jpg',
    }
  },
  {
    name: 'Mineral Water',
    image: {
        medium: 'img/mineral-water-300x300.jpg',
        small: 'img/mineral-water-150x150.jpg',
    }
  },
  {
    name: 'French Fries',
    image: {
        medium: 'img/french-fries-300x300.jpg',
        small: 'img/french-fries-150x150.jpg',
    }
  },
]

ReactDOM.render(<App products={products} />, document.getElementById('root'));

ControlItem.js

import React from 'react';

// Fungsi helper untuk membuat huruf awal string menjadi kapital
const titleize = (str) => str.charAt(0).toUpperCase() + str.substring(1, str.length);

const ControlItem = ({ layout, activeLayout, onDepressed }) => (
  <a
    href="javascript:void()"
    className={layout === activeLayout ? 'active' : ''}
    onClick={onDepressed.bind(this, layout)}
  >
    {titleize(layout)} View
  </a>
);

export default ControlItem;

List.js

import React, {Component} from 'react';

const List = ({ layout, products }) => (
  <ul className={layout}>
    {products.map((product, index) => {

      return (
        <li>
          <img src={layout === 'list' ? product.image.small : product.image.medium} />
          {
            layout === 'list' ? <p>{product.name}</p> : ''
          }
        </li>
      );
    })}
  </ul>
);

export default List;

App.js

    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './App.css';
    import ControlItem from './ControlItem';
    import List from './List';

    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          layout: 'list',
        };

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

      switchLayout(layout) {
        this.setState({
          layout,
        });
      }

      render() {
        const { layout } = this.state;
        const { products } = this.props;

        return (
          // Disini kita membuat komponen-komponen kecil yang memiliki tugas spesifik,
          // dengan begitu aplikasi kita lebih mudah untuk dikelola

          <div className="app">
            <div className="controls">

              {/* ControlItem adalah komponen untuk berganti layout */}
              <ControlItem
                layout="list"
                activeLayout={layout}
                onDepressed={this.switchLayout}
              />

              <ControlItem
                layout="grid"
                activeLayout={layout}
                onDepressed={this.switchLayout}
              />
            </div>

            {/* Kita hanya mengirim props yang diperlukan oleh List */}
            <List
              layout={layout}
              products={products}
            />
          </div>
        );
      }
    }

export default App;

Demo Switchable Grids

Perhatikan juga ControlItem dan List tidak menggunakan Class. Sebuah komponen React tidak harus Class tapi bisa juga berupa fungsi yang hanya mengembalikan JSX yang akan di-render. Kapan kita menggunakan Class dan kapan kita menggunakan Fungsi? Jika suatu komponen hanya melakukan render maka gunakan fungsi, tetapi jika komponen tersebut memiliki fungsi sendiri dan atau menggunakan React Component Lifecycle maka gunakan Class.

Kesimpulan

React membuat kita bisa membangun aplikasi yang memiliki struktur yang jelas dan juga reusability yang luar biasa. Dan berkat virtual DOM nya, aplikasi komplex bisa tetap cepat dengan React.

Masih banyak yang perlu kita pelajari tentan React. Silahkan pelajari juga link-link berikut: