ReactJS cho người mới bắt đầu
Bài đăng này đã không được cập nhật trong 3 năm
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ính và sự 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 choComponent
. Hay tưởng tượngComponent
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ânComponent
sẽ không tự thay đổi đượcprops
của nó. -
State
là những thuộc tính bên trong.State
chỉ được sử dụng bên trongComponent
và bên ngoàiComponent
không thể truy xuất đếnState
của nó. Để thay đổistate
ta sử dụng hàmthis.setState({stateName: 'gia tri'})
, biến truyền vào làobject
chứa cácstate
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 UserComponent
và render
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
. KhiReactDOM
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 componentUserComponent
. Bạn chỉ cần hiểu đơn giản như thế này:React Component
là class,React Element
là object, 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ó propTypes và defaultProps để 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àmshouldComponentUpdate()
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àmshouldComponentUpdate()
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ùngthis.setState
. Đơn giản là khi sử dụngthis.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àmasync
(bất đồng bộ), nên truy cậpthis.state
ngay sau khisetState
sẽ không nhận được giá trị mới củathis.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àmshouldComponentUpdate()
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ônrender
lại khiState
hoặcProps
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
khiState
hoặcProps
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ácstudent
trong statestudents
để tạo các Element tương ứng vớiStudentComponent
.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>
<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