Tự học React JS trọn bộ kiến thức cơ bản – phần 3

Đây là phần cuối trong chuỗi series Tự học React JS trọn bộ kiến thức cơ bản. Chúng ta cùng tìm hiểu ngay nào


1067

Events

 Về cơ bản không có nhiều khác biệt trong cách xử lý sự kiện giữa React và Javascript. Với Javascript, khi sự kiện xảy ra một hàm sẽ được gọi để thực thi. Nhưng với React, khi sự kiện xẩy ra, sẽ có một phương thức của Component được gọi.

Ví dụ đơn giản

Đây là một ví dụ đơn giản trong đó chúng ta sử dụng component. Chúng ta thêm sự kiện onClick sẽ kích hoạt chức năng updateState sau khi click vào button.

import React from 'react';
class App extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         data: 'Initial data...'
      }
      this.updateState = this.updateState.bind(this);
   };
   updateState() {
      this.setState({data: 'Data updated...'})
   }
   render() {
      return (
         <div>
            <button onClick = {this.updateState}>CLICK</button>
            <h4>{this.state.data}</h4>
         </div>
      );
   }
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App/>, document.getElementById('app'));

Kết quả sẽ như sau:

React Events Simple

Events Child(events con)

Khi chúng ta cần cập nhật state của component cha từ component con của nó. Chúng ta tạo event (updateState) trong component cha và truyền nó dưới dạng prop (updateStateProp) cho component con nơi gọi nó trong React.

import React from 'react';
class App extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         data: 'Initial data...'
      }
      this.updateState = this.updateState.bind(this);
   };
   updateState() {
      this.setState({data: 'Data updated from the child component...'})
   }
   render() {
      return (
         <div>
            <Content myDataProp = {this.state.data}
               updateStateProp = {this.updateState}></Content>
         </div>
      );
   }
}
class Content extends React.Component {
   render() {
      return (
         <div>
            <button onClick = {this.props.updateStateProp}>CLICK</button>
            <h3>{this.props.myDataProp}</h3>
         </div>
      );
   }
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App/>, document.getElementById('app'));

Kết quả sẽ như thế này khi click vào button

React Events Child

Refs React

Trong data flow của React, props là cách để các parent components tương tác với các child components. Để updated child componnent, bạn cần re-render nó với các props mới. Nhưng sẽ có một số trường hợp cần bắt buộc phải updated các child component bên ngoài data flow của React. Các child component có thể là 1 instance của React Component hoặc có thể là DOM element. Cho những trường hợp này, chúng ta có thể sử dụng react Refs.

Ví dụ

Ví dụ sau đây cho thấy cách sử dụng ref để xóa field đầu vào. Hàm ClearInput tìm kiếm phần tử có giá trị ref = “myInput”, đặt lại state và thêm con trỏ cho nó sau khi click vào button.

import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         data: ''
      }
      this.updateState = this.updateState.bind(this);
      this.clearInput = this.clearInput.bind(this);
   };
   updateState(e) {
      this.setState({data: e.target.value});
   }
   clearInput() {
      this.setState({data: ''});
      ReactDOM.findDOMNode(this.refs.myInput).focus();
   }
   render() {
      return (
         <div>
            <input value = {this.state.data} onChange = {this.updateState}
               ref = "myInput"></input>
            <button onClick = {this.clearInput}>CLEAR</button>
            <h4>{this.state.data}</h4>
         </div>
      );
   }
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App/>, document.getElementById('app'));

Khi bạn nhập dữ liệu vào input, và nhấn button clear, sẽ có kết quả sau:

React Refs Example

Key React

Key react rất hữu ích khi làm việc với các component react được tạo động hoặc khi danh sách của bạn bị thay đổi bởi người dùng. Đặt key sẽ giữ cho các component của bạn được xác định duy nhất sau khi thay đổi.

Ví dụ

Hãy tự động tạo Content với chỉ mục duy nhất (i). Hàm map sẽ tạo ba phần tử từ mảng dữ liệu của chúng ta. Vì giá trị key cần là duy nhất cho mọi phần tử, chúng tasẽ gán i làm key cho mỗi phần tử được tạo.

import React from 'react';
class App extends React.Component {
   constructor() {
      super();
      this.state = {
         data:[
            {
               component: 'First...',
               id: 1
            },
            {
               component: 'Second...',
               id: 2
            },
            {
               component: 'Third...',
               id: 3
            }
         ]
      }
   }
   render() {
      return (
         <div>
            <div>
               {this.state.data.map((dynamicComponent, i) => <Content
                  key = {i} componentData = {dynamicComponent}/>)}
            </div>
         </div>
      );
   }
}
class Content extends React.Component {
   render() {
      return (
         <div>
            <div>{this.props.componentData.component}</div>
            <div>{this.props.componentData.id}</div>
         </div>
      );
   }
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App/>, document.getElementById('app'));

Kết quả sẽ như sau:

React Keys Example

Router React

React Router là một thư viện định tuyến (routing) tiêu chuẩn trong React. Nó giữ cho giao diện của ứng dụng đồng bộ với URL trên trình duyệt. React Router cho phép bạn định tuyến “luồng dữ liệu”(data flow) trong ứng dụng của bạn một cách rõ ràng.

Sau đây là các bước để sử dụng router

Cài đặt Router React

Cách đơn giản để cài đặt router react là chạy đoạn code trên terminal

npm install react-router

Tạo Components

Trong bước này, chúng ta tạo ra bốn component.Component react sẽ được sử dụng làm menu tab. Ba component khác Home(Trang chủ), About(Giới thiệu) và Contact(Liên hệ) được hiển thị khi Router thay đổi.

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Link, browserHistory, IndexRoute } from 'react-router'
class App extends React.Component {
   render() {
      return (
         <div>
            <ul>
            <li>Home</li>
            <li>About</li>
            <li>Contact</li>
            </ul>
            {this.props.children}
         </div>
      )
   }
}
export default App;
class Home extends React.Component {
   render() {
      return (
         <div>
            <h1>Home...</h1>
         </div>
      )
   }
}
export default Home;
class About extends React.Component {
   render() {
      return (
         <div>
            <h1>About...</h1>
         </div>
      )
   }
}
export default About;
class Contact extends React.Component {
   render() {
      return (
         <div>
            <h1>Contact...</h1>
         </div>
      )
   }
}
export default Contact;

Thêm Router

Bây giờ, chúng ta sẽ thêm router vào app. Thay vì render app như trong ví dụ trước, lần này Router sẽ được hiển thị. Chúng ta cũng sẽ thiết lập các component cho mỗi router.

ReactDOM.render((
   <Router history = {browserHistory}>
      <Route path = "/" component = {App}>
         <IndexRoute component = {Home} />
         <Route path = "home" component = {Home} />
         <Route path = "about" component = {About} />
         <Route path = "contact" component = {Contact} />
      </Route>
   </Router>
), document.getElementById('app'))

Khi ứng dụng được khởi động, chúng ta sẽ thấy ba liên kết có thể nhấp vào để thay đổi giá trị hiển thị.

Clickable Links

Flux(thông lượng) Concept

Flux là một khái niệm lập trình, trong đó dữ liệu một hướng. Dữ liệu này vào ứng dụng và đi theo một hướng cho đến khi nó được hiển thị trên màn hình.

Flux Elements

  • Actions – được gửi đến bộ điều phối để kích hoạt luồng dữ liệu.
  • Dispatcher – Đây là một trung tâm của app. Tất cả các dữ liệu được gửi và gửi đến các store.
  • Store – Là nơi chứa state của app và logic. Mỗi store đang duy trì một state cụ thể và sẽ cập nhật khi cần.
  • View – sẽ nhận dữ liệu từ store và re-render lại app
Flux Concept Image

Ưu điểm

  • Luồng dữ liệu định hướng đơn nên rất dễ hiểu.
  • Dễ bảo trì app, project react hơn

Sử dụng flux trong react

Để sử dụng flux , chúng ta cần cài đặt Redux theo bước sau

npm install --save react-redux

Tạo các folder và các file như sau :

React Redux Folder Structure

Actions là object JavaScript sử dụng thuộc tính type để thông báo về dữ liệu sẽ được gửi đến store. Chúng ta đang xác định action ADD_TODO sẽ được sử dụng thêm mục mới vào danh sách

export const ADD_TODO = 'ADD_TODO'
let nextTodoId = 0;
export function addTodo(text) {
   return {
      type: ADD_TODO,
      id: nextTodoId++,
      text
   };
}

Mặc dù actions chỉ kích hoạt các thay đổi trong app, thì reducers chỉ định những thay đổi đó. Chúng ta sử dụng câu lệnh chuyển đổi để tìm kiếm actions ADD_TODO. Bộ reducers là một hàm lấy hai tham số (state  và action) để tính toán và trả về state cập nhật.

Hàm đầu tiên được sử dụng để tạo một mục mới, trong khi hàm thứ hai sẽ đẩy mục đó vào danh sách.

Đến cuối cùng, chúng ta sử dụng hàm hỗ trợ combineReducers để có thể thêm bất kỳ reducers mới và sử dụng trong tương lai.

import { combineReducers } from 'redux'
import { ADD_TODO } from '../actions/actions'
function todo(state, action) {
   switch (action.type) {
      case ADD_TODO:
         return {
            id: action.id,
            text: action.text,
         }
      default:
         return state
   }
}
function todos(state = [], action) {
   switch (action.type) {
      case ADD_TODO:
         return [
            ...state,
            todo(undefined, action)
         ]
      default:
         return state
   }
}
const todoApp = combineReducers({
   todos
})
export default todoApp

Store là nơi chứa state của ứng dụng. Nó rất dễ dàng để tạo ra store khi bạn có reducers.

import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './App.jsx'
import todoApp from './reducers/reducers'
let store = createStore(todoApp)
let rootElement = document.getElementById('app')
render(
   <Provider store = {store}>
      <App />
   </Provider>,
   rootElement
)

App là component root của app. Function này lấy chức năng chọn làm đối số. Chọn function lấy state từ store và trả về props  (viewerTodos) mà chúng ta có thể sử dụng trong components của mình.

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addTodo } from './actions/actions'
import AddTodo from './components/AddTodo.js'
import TodoList from './components/TodoList.js'
class App extends Component {
   render() {
      const { dispatch, visibleTodos } = this.props
      return (
         <div>
            <AddTodo onAddClick = {text =>dispatch(addTodo(text))} />
            <TodoList todos = {visibleTodos}/>
         </div>
      )
   }
}
function select(state) {
   return {
      visibleTodos: state.todos
   }
}
export default connect(select)(App);
import React, { Component, PropTypes } from 'react'
export default class AddTodo extends Component {
   render() {
      return (
         <div>
            <input type = 'text' ref = 'input' />
            <button onClick = {(e) => this.handleClick(e)}>
               Add
            </button>
         </div>
      )
   }
   handleClick(e) {
      const node = this.refs.input
      const text = node.value.trim()
      this.props.onAddClick(text)
      node.value = ''
   }
}
import React, { Component, PropTypes } from 'react'
export default class Todo extends Component {
   render() {
      return (
         <li>
            {this.props.text}
         </li>
      )
   }
}
import React, { Component, PropTypes } from 'react'
import Todo from './Todo.js'
export default class TodoList extends Component {
   render() {
      return (
         <ul>
            {this.props.todos.map(todo =>
               <Todo
                  key = {todo.id}
                  {...todo}
               />
            )}
         </ul>
      )
   }
}

Sau khi đã thêm 3 item, kết quả sẽ như sau :

React Redux Example

Animations(Hiệu ứng)

Cách cơ bản và dễ hình dung nhất, sử dụng class CSS, add/remove các class này để thực thi một animation. Để sử dụng animation trong react cần làm theo những bước sau

npm install react-addons-css-transition-group
//tạo file css/style.css
mkdir css/
cd css/
touch style.css

Để sử dụng css , chúng ta cần gắn vào header trong file index.html

<!DOCTYPE html>
<html lang = "en">
   <head>
      <link rel = "stylesheet" type = "text/css" href = "./style.css">
      <meta charset = "UTF-8">
      <title>React App</title>
   </head>
   <body>
      <div id = "app"></div>
      <script src = 'index_bundle.js'></script>
   </body>
</html>

Chúng ta sẽ tạo component react cơ bản. Phần tử ReactCSSTransitionGroup  sẽ được sử dụng wrappercomponent mà chúng ta muốn tạo animation

import React from 'react';
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');
class App extends React.Component {
   render() {
      return (
         <div>
            <ReactCSSTransitionGroup transitionName = "example"
               transitionAppear = {true} transitionAppearTimeout = {500}
               transitionEnter = {false} transitionLeave = {false}>
               <h1>My Element...</h1>
            </ReactCSSTransitionGroup>
         </div>
      );
   }
}
export default App;
import React from 'react'
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
.example-appear {
   opacity: 0.04;
}
.example-appear.example-appear-active {
   opacity: 2;
   transition: opacity 50s ease-in;
}

Chúng ta sẽ được hiệu ứng mờ như bên dưới

Tiếp theo, chúng ta có thể thực hiện animation khi thêm hoặc xóa phần tử trong mảng theo cách sau

import React from 'react';
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');
class App extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         items: ['Item 1...', 'Item 2...', 'Item 3...', 'Item 4...']
      }
      this.handleAdd = this.handleAdd.bind(this);
   };
   handleAdd() {
      var newItems = this.state.items.concat([prompt('Create New Item')]);
      this.setState({items: newItems});
   }
   handleRemove(i) {
      var newItems = this.state.items.slice();
      newItems.splice(i, 1);
      this.setState({items: newItems});
   }
   render() {
      var items = this.state.items.map(function(item, i) {
         return (
            <div key = {item} onClick = {this.handleRemove.bind(this, i)}>
               {item}
            </div>
         );
      }.bind(this));
      return (
         <div>
            <button onClick = {this.handleAdd}>Add Item</button>
            <ReactCSSTransitionGroup transitionName = "example"
               transitionEnterTimeout = {500} transitionLeaveTimeout = {500}>
               {items}
            </ReactCSSTransitionGroup>
         </div>
      );
   }
}
export default App;
import React from 'react'
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
.example-enter {
   opacity: 0.04;
}
.example-enter.example-enter-active {
   opacity: 5;
   transition: opacity 50s ease-in;
}
.example-leave {
   opacity: 1;
}
.example-leave.example-leave-active {
   opacity: 0.04;
   transition: opacity 50s ease-in;
}

Khi chạy app react và click vào button Add item sẽ hiển thị như sau

React Animations Prompt

Khi click OK, My new Item.. sẽ được thêm vào list và có hiệu ứng mờ

React Animations Enter

Còn đây là hiệu ứng khi xóa Item 3

React Animations Leave

Higher Order Components(HOC) React

Các Higher Order Components là các hàm JavaScript được sử dụng để thêm các function bổ sung vào components hiện có.

Chúng ta hãy xem một ví dụ đơn giản để dễ dàng hiểu cách thức hoạt động của khái niệm này. MyHOC là Higher Order Components được sử dụng để truyền dữ liệu đến MyComponent.

import React from 'react';
var newData = {
   data: 'Data from HOC...',
}
var MyHOC = ComposedComponent => class extends React.Component {
   componentDidMount() {
      this.setState({
         data: newData.data
      });
   }
   render() {
      return <ComposedComponent {...this.props} {...this.state} />;
   }
};
class MyComponent extends React.Component {
   render() {
      return (
         <div>
            <h1>{this.props.data}</h1>
         </div>
      )
   }
}
export default MyHOC(MyComponent);

Khi run app, chúng ta sẽ thấy dữ liệu đó được chuyển đến MyComponent.

Tổng kết

Chúng ta tổng kết những điều quan trọng trong việc sử dụng React và giúp bạn hiểu rõ hơn.

  • State – Nên tránh sử dụng và sử dụng càng ít càng tốt. Bất cứ khi nào chúng ta có một nhóm components cần cùng một dữ liệu, chúng ta nên đặt element container xung quanh lưu giữ trạng thái.
  • PropTypes – phải luôn luôn được khai báo. Điều này sẽ giúp theo dõi tất cả các props trong ứng dụng và hữu ích cho bất kỳ developer nào làm việc trong các dự án tương tự.
  • Render – Hầu hết logic của ứng dụng nên được bỏ vào bên trong method render. Chúng ta nên giảm thiểu logic trong các vòng đời component và di chuyển logic đó vào trong render. Nếu chúng ta cần tính toán một cái gì đó từ state hoặc props, có thể thực hiện nó bên trong method render.
  • Composition – React đề nghị sử dụng một nguyên tắc đó là một components chỉ nên chịu trách nhiệm cho một chức năng. Nếu component có nhiều hơn một chức năng, chúng ta nên cấu trúc lại và tạo một component mới cho mọi chức năng.
  • Higher Order Components (HOC) – Các phiên bản React trước đây cung cấp mixins để xử lý các chức năng có thể tái sử dụng. Vì mixins hiện không được dùng nữa, một trong những giải pháp là sử dụng HOC.

Like it? Share with your friends!

1067