SSV
+13

ReactJS cho người mới bắt đầu

Xin chào các bạn! Sau một thời gian tìm hiểu về ReactJs, hôm nay mình xin viết một bài về những gì cơ bản nhất của React để các bạn có thể sử dụng được nó. Bài viết này sẽ không giải thích ReactJs là gì hay ưu nhược điểm của nó hoặc là so sánh nó với các thư viện khác. Bài viết sẽ giúp bạn có thể tự tay dùng React để viết một để viết Application. Các nội dung chính như sau:

  • Chuẩn bị môi trường
  • Cài các gói cần thiết
  • Tìm hiểu về React
  • Cách sử dụng
  • Kết luận
  • Tài liệu tham khảo

Chuẩn bị môi trường

Bài viết sử dụng Laravel 5.4 do vậy chúng ta cần có composer, php, apache, mysql. Laravel 5.4 sẽ sử dụng webpack để build code react. Các phiên bản trước sẽ dùng gulp nên việc build sẽ khác nhưng cách thức hoạt động của react thì không thay đổi (tất nhiên rồi 😃 ). Nếu bạn đang dùng bản laravel cũ hơn chỉ cần chỉnh phần build code theo phiên bản mình đang sử dụng thôi.

Để cài được các gói hỗ trợ cho React ta cần phải có nodejs - npm.

Như vậy là đã chuẩn bị xong môi trường. Tiếp theo ta cần phải cài các gói cần thiết để có thể viết được một Application dùng React.

Cài các gói cần thiết

Khi sử dụng React ta cần cài các gói sau:

  • "react": "^15.4.1", Gói này là gói react. Vâng, chúng ta đang sử dụng nó mà. Và tất nhiên nó phải có mặt ở đây.
  • "react-autobind": "^1.0.6", Gói này sẽ giúp bạn tự động bind this khi truyền hàm ra ngoài. Mình sẽ nói rõ hơn ở phần dưới.
  • "react-dom": "^15.4.1", Gói này sẽ giúp ta kết nối react với cây DOM bên ngoài. Thường mình chỉ dùng để render React element vào DOM.
  • "react-router": "^3.0.0", Gói này sẽ giúp thao tác với các router, lịch sử truy cập, điều hướng trang.
  • "react-widgets": "^3.4.5", Gói này sẽ cung cấp cho ta một số thành phần có giao diện sẵn

Thêm các gói cài đặt vào file package.json

{
  "private": true,
  "scripts": {
    "dev": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --watch-poll --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "axios": "^0.15.3",
    "bootstrap-sass": "^3.3.7",
    "cross-env": "^3.2.3",
    "jquery": "^3.1.1",
    "laravel-mix": "^0.8.3",
    "lodash": "^4.17.4",
    "vue": "^2.1.10",
    "material-ui": "^0.16.5",
    "react": "^15.4.1",
    "react-autobind": "^1.0.6",
    "react-dom": "^15.4.1",
    "react-router": "^3.0.0",
    "react-tap-event-plugin": "^2.0.1",
    "react-widgets": "^3.4.5"
  }
}

sau đó chạy npm install

Như vậy là phần chuẩn bị đã xong. Giờ bắt đầu vào việc tạo App dùng React nào.

React

Tổng quan

Nếu một trang HTML được cấu tạo từ các thẻ thì React được cấu tạo từ các Component.

Component cũng có thành phần cha, thành phần con và thuộc tính như thẻ trong HTML vậy.

Component mình chia làm 2 loại: Component mặc định và Component customize.

  • Component mặc định thì nó sẽ tương ứng với một thẻ HTML (ví dụ như:
    , <span></span>). Nó có đầy đủ các thuộc tínhsự kiện của thẻ HTML.
  • Component customize là component được viết dựa trên các component mặc định. Với các thuộc tính và sự kiện mình tự quy định.

Prop và State

Đây là 2 cách để lưu trữ dữ liệu trong Component.

  • Props là những thuộc tính mà ta truyền vào cho Component. Hay tưởng tượng Component là một thẻ HTML thì props chính là các thuộc tính (như: id, style, class, onclick, ...) được truyền vào thẻ đó. Bản thân Component sẽ không tự thay đổi được props của nó.

  • State là những thuộc tính bên trong. State chỉ được sử dụng bên trong Component và bên ngoài Component không thể truy xuất đến State của nó. Để thay đổi state ta sử dụng hàm this.setState({stateName: 'gia tri'}), biến truyền vào là object chứa các state cần thay đổi.

Chương trình React đầu tiên

Viết file app.js

import React from 'react';
import ReactDOM from 'react-dom';

class UserComponent extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            name: props.name
        }
    }

    render() {
        return (
            <div>Hello {this.state.name}!</div>
        );
    }
}

UserComponent.propTypes = {
    name: React.PropTypes.string,
};

UserComponent.defaultProps = {
    name: 'Hoang Hoi'
}

ReactDOM.render(<UserComponent/>, document.getElementById('app'));

Đoạn code trên có chức năng tạo một React Element UserComponentrender nó vào HTML Element có id là app. Phân tích một chút về Component vừa tạo:

  • Có một state là name và một prop cũng tên là name luôn
  • Kiểu prop truyền vào là string và có giá trị mặc định là 'Hoang Hoi'
  • Hàm render sẽ trả về một React element div. Khi ReactDOM render thì nó sẽ thành thẻ div tương ứng.
  • Trong div có in ra dòng chữ Hello !. {this.state.name} đây là cách nhúng mã javascript vào jsx. Lại xuất hiện thêm một khái niệm nữa. Jsx là gì? JSX = JS + XML. Có nghĩa là viết Javascript theo phong cách XML. <UserComponent/> chính là tạo một element tương ứng với component UserComponent. Bạn chỉ cần hiểu đơn giản như thế này: React Componentclass, React Elementobject, JSX giúp tạo object từ class dễ dàng, trông đẹp hơn.
  • Để render một component ra DOM Element ta sử dụng react-dom. Các tham số cần truyền vào là React Element và DOM Element.

Việc tiếp theo là tạo một view để hiển thị. Tạo view Tạo file app.blade.php trong thư mục view với nội dung như sau:

<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Styles -->
    <link href="{{ mix('css/app.css') }}" rel="stylesheet">

    <!-- Scripts -->
    <script>
        window.Laravel = {!! json_encode([
            'csrfToken' => csrf_token(),
        ]) !!};
    </script>
</head>
<body>
    <div id="app">
    </div>

    <!-- Scripts -->
    <script src="{{ mix('js/app.js') }}"></script>
</body>
</html>

Lưu ý khi tạo file view

  • Phải có một thẻ <div> có id là app để React DOM render code vào.
  • Code React sau khi được build và mix sẽ được nhúng vào trang qua thẻ <script src="{{ mix('js/app.js') }}"></script>.

Build react code Như đã nói ở trên Laravel 5.4 dùng webpack để build code. Việc cài đặt để build code React cực kì đơn giản. Mở file webpack.mix.js ra và sửa thành như sau:

const { mix } = require('laravel-mix');

mix.react('resources/assets/js/app.js', 'public/js')
    .sass('resources/assets/sass/app.scss', 'public/css')
    .version();

laravel-mix có thể tự động cài đặt các plug-in Babel cần thiết để hỗ trợ React.

Các lệnh:

  • npm run dev : lệnh buil code:
  • npm run watch : buil code khi có thay đổi: (Lệnh này sẽ build code xong và lắng nghe sự thay đổi code. Khi code có thay đổi, webpack sẽ tự động chạy để build lại code cho mình. Không cần phải chạy lại lệnh nữa.)
  • npm run production : build code và minify code (loại bỏ các dòng thừa - nén code)

Thế là chúng ta đã tạo được một trang sử dụng React đầu tiên. Chạy lên nếu hiện ra dòng chữ Hello Hoang Hoi! là bạn đã thành công.

Bây giờ chúng ta hãy truyền tên khác vào nhé: Sửa dòng ReactDOM.render(<UserComponent/>, document.getElementById('app')); một chút thành ReactDOM.render(<UserComponent name="Ten ban"/>, document.getElementById('app'));. Đây là cách ta truyền props vào Component đó. Cũng đơn giản phải không. 😃

Build lại chương trình nào. Bạn hãy nhớ là mỗi khi thay đổi code. Muốn chương trình chạy theo code mới thì phải build lại. Nếu bạn dùng lệnh npm run watch thì không cần làm gì cả nó sẽ tự động build khi code thay đổi. Còn nếu chạy lệnh khác thì chịu khó chạy thêm lần nữa để nó build.

Component

Một component đầy đủ các thuộc tính và hàm được viết như sau:

import React from 'react';

class UserComponent extends Component {
    constructor(props){
        // Việc sử dụng super(props) là để có thể sử dụng this.props trong phạm vi hàm constructor này
        super(props);
        // Có thể thực hiện việc thiết lập state cho component
        this.state = {
            name: props.name
        }
    }

    componentWillMount() {
    // Thực hiện một số tác vụ, khi component sẽ được mount
    // hàm này chỉ thực hiện 1 lần duy nhất
    }

    componentDidMount() {
    // Thực hiện một số tác vụ, hàm này chỉ thực hiện 1 lần duy nhất
    // Hàm này được gọi để thông báo component đã tồn tại trên DOM,
    // Tức là hàm render đã chạy
    // Từ đó các thao tác trên DOM sẽ có thể thực hiện bình thường đối với component này
    }

    componentWillUnmount() {
    // Hàm này thực hiện một lần duy nhất,
    // Khi component sẽ unmount
    // Hàm này hữu dụng khi bạn cần xoá các timer hoặc EventListener không còn sử dụng
    }

    componentWillReceiveProps(nextProps) {
    // Hàm này thực hiện liên tục mỗi khi props thay đổi
    // Có thể sử dụng với mục đích:
    // (1) Sử dụng để thay đổi trạng thái (state) của component phụ thuộc props.
    // (2) Sử dụng các kết quả, khởi tạo biến có tính chất async (bất đồng bộ).
    }

    shouldComponentUpdate(nextProps, nextState) {
    // Hàm này thực hiện khi state và props thay đổi
    // Hàm này sẽ trả về kết quả true/false,
    // Bạn sẽ cần sử dụng đến hàm này để xử lý xem có cần update component không
    }

    componentWillUpdate(nextProps, nextState) {
    // Hàm này thực hiện dựa vào kết quả của hàm trên (shouldComponentUpdate)
    // Nếu hàm trên trả về false, thì React sẽ không gọi hàm này
    }

    componentDidUpdate(prevProps, prevState) {
    // Hàm này thực hiện sau khi component được render lại
    }

    render() {
        // Hàm này phải trả về null, React component hoặc mảng các React component
        return (
            <div>{/* Component được render */}</div>
        );
    }
}

UserComponent.propTypes = {
    //Khai báo kiểu biến cho props
    name: React.PropTypes.string,
};

UserComponent.defaultProps = {
    //Khai báo giá trị mặc định cho props
    name: 'Hoang Hoi'
}

export default UserComponent;

Trên đây là các hàm và thuộc tính cơ bản của Component. Phần chức năng mình đã trình bày trong comment.

Component không cần phải có tất cả các hàm và phương thức trên. Nó chỉ cần có hàm khởi tạo (constructor) với hàm render (render) là có thể chạy ngon lành rồi. Vì vậy chỉ sử dụng các hàm khác khi cần thiết. Không nhất thiết phải viết tất cả vào.

Điều đặc biệt cần chú ý là nếu có sử dụng props trong Component thì phải có propTypesdefaultProps để xác định xem mình cần truyền vào props nào và kiểu dữ liệu như thế nào.

Để hiểu rõ hơn về vòng đời của React component chúng ta sẽ phân tích sơ đồ sau:

Khởi tạo Component

  • Khởi tạo Class (đã thừa kế từ class Component của React)
  • Khởi tạo giá trị mặc định cho Props (defaultProps)
  • Khởi tạo giá trị mặc định cho State (trong hàm constuctor)
  • Gọi hàm componentWillMount()
  • Gọi hàm render()
  • Gọi hàm componentDidMount()

Khi State thay đổi

  • Cập nhật giá trị cho state
  • Gọi hàm shouldComponentUpdate()
  • Gọi hàm componentWillUpdate() – với điều kiện hàm shouldComponentUpdate() trả về true
  • Gọi hàm render()
  • Gọi hàm componentDidUpdate()

Khi Props thay đổi

  • Cập nhật giá trị cho props
  • Gọi hàm componentWillReceiveProps()
  • Gọi hàm shouldComponentUpdate()
  • Gọi hàm componentWillUpdate() – với điều kiện hàm shouldComponentUpdate() trả về true
  • Gọi hàm render()
  • Gọi hàm componetDidUpdate()

Khi Unmount component

  • Gọi hàm componentWillUnmount()

Chú ý

  • Tại sao chúng ta không dùng this.state = 'gia tri' mà phải dùng this.setState. Đơn giản là khi sử dụng this.setState, chính là kích hoạt danh sách các phương thức thuộc vòng đời của component (shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate).
  • Bạn luôn phải nhớ rằng, this.setState là hàm async (bất đồng bộ), nên truy cập this.state ngay sau khi setState sẽ không nhận được giá trị mới của this.state.
  • Nếu bạn muốn render lại component có thế sử dụng hàm component.forceUpdate(callback). Hàm này sẽ bỏ qua việc gọi hàm shouldComponentUpdate() trong quá trình thực hiện render.
  • Hàm shouldComponentUpdate() mặc định luôn trả về true tức là component luôn render lại khi State hoặc Props thay đổi (chỉ cần thực hiện gán giá trị, cho dù giá trị cũ và mới bằng nhau). Bạn có thể viết code ở hàm này để chỉ định lúc nào quá trình render cần hoặc không cần diễn ra.
  • Component chỉ render khi State hoặc Props thay đổi.
  • Khi nút cha render lại thì tất cả các nút con của nó đều sẽ được render lại.

Sử dụng

Mình đã trình bày xong một số thành phần cơ bản của component và cách viết chương trình react đầu tiên. Giờ chúng ta sẽ cùng tìm hiểu cách sử dụng các thành phần của react trong từng ví dụ cụ thể nhé.

Viết hai Component lồng nhau

Việc này đơn giản như là viết hai thẻ HTML lồng nhau vậy. Tạo file components/student-component.js

import React from 'react';

class StudentComponent extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            name: props.name
        }
    }

    render() {
        return (
            <li>{this.state.name}</li>
        );
    }
}

StudentComponent.propTypes = {
    name: React.PropTypes.string,
};

StudentComponent.defaultProps = {
    name: 'Hoang Hoi'
}

export default StudentComponent;

Tạo file components/school-component.js

import React from 'react';
import Student from './student-component'

class SchoolComponent extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            students: [
                {id: 1, name: 'Hoang Hoi'},
                {id: 2, name: 'Nguyen Luong'},
                {id: 3, name: 'Nguyen Kim'},
                {id: 4, name: 'Le Duy'},
                {id: 5, name: 'Ngo Xuan'},
            ]
        }
    }

    render() {
        let students = this.state.students;
        return (
            <ul>
                {
                    students.map((student) => {
                        return <Student name={student.name} key={student.id}/>
                    })
                }
            </ul>
        );
    }
}

export default SchoolComponent;

Tạo file app.js

import React from 'react';
import ReactDOM from 'react-dom';
import School from './components/school-component';

class App extends React.Component {
    constructor(props){
        super(props);
    }

    render() {
        return (
            <div>
                <School />
            </div>
        );
    }
}

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

** Giải thích một chút **

  • SchoolComponent sẽ map (đây là một hàm giống foreach trong php đấy) tất cả các student trong state students để tạo các Element tương ứng với StudentComponent.
  • App sẽ render một div element và trong đó là School element.
  • Kết quả thu được mã HTML sau:
<div id="app">
    <div data-reactroot="">
        <ul>
            <li>Hoang Hoi</li>
            <li>Nguyen Luong</li>
            <li>Nguyen Kim</li>
            <li>Le Duy</li>
            <li>Ngo Xuan</li>
        </ul>
    </div>
</div>
  • <Student name={student.name} key={student.id}/> ở trong danh sách các element cùng cấp nên phải có key để phân biệt với anh chị em của nó.

Truyền phương thức prop vào component

Chúng ta đã truyền được biến vào component rất đơn giản. Còn phương thức thì sao? Truyền phương thức vào component cũng đơn giản không kém. Nhưng có một số điểm cần lưu ý khi truyền phương thức ra bên ngoài nếu ta sử dụng this ở trong hàm thì chúng ta phải bind từ khóa this. Để đơn giản ta sử dụng react-autobind cho tất cả các hàm. File components/student-component.js

import React from 'react';
import autobind from 'react-autobind';

class StudentComponent extends React.Component {
    constructor(props){
        super(props);
        autobind(this);
        this.state = {
            name: props.name
        }
    }

    render() {
        this.props.onClick();
        return (
            <li>
                <span>{this.state.name}</span>&nbsp;&nbsp;
                <span onClick={this.props.onClick}>Click me</span>
            </li>
        );
    }
}

StudentComponent.propTypes = {
    name: React.PropTypes.string,
};

StudentComponent.defaultProps = {
    name: 'Hoang Hoi'
}

export default StudentComponent;

File components/school-component.js

import React from 'react';
import autobind from 'react-autobind';
import Student from './student-component'

class SchoolComponent extends React.Component {
    constructor(props){
        super(props);
        autobind(this);
        this.state = {
            students: [
                {id: 1, name: 'Hoang Hoi'},
                {id: 2, name: 'Nguyen Luong'},
                {id: 3, name: 'Nguyen Kim'},
                {id: 4, name: 'Le Duy'},
                {id: 5, name: 'Ngo Xuan'},
            ]
        }
    }

    handleClick(){
        console.log(this);
    }

    render() {
        let students = this.state.students;
        return (
            <ul>
                {
                    students.map((student) => {
                        return <Student
                            name={student.name}
                            key={student.id}
                            onClick={this.handleClick}
                        />
                    })
                }
            </ul>
        );
    }
}

export default SchoolComponent;

File app.js giữ nguyên

Kết quả sau khi chạy sẽ luôn log ra School Element. Bạn hãy thử bỏ autobind(this) đi xem nó sẽ log ra gì nhé. 😃

Kết luận

Trên đây mình đã trình bày cách sử dụng React cơ bản nhất. Những khái niệm cần nắm vững cũng như cách hoạt động của nó. Hi vọng bài viết sẽ giúp ích được cho các bạn.

Tài liệu tham khảo


All Rights Reserved