Làm quen với Redux trong rails app.

Để bắt đầu với redux, bạn cần biết:

Redux là một thư viện do chính Facebook tạo ra, nó đc Facebook định nghĩa là: "predictable state container for JavaScript apps" (tạm dịch là một lớp quản lý state cho ứng dụng Javascript) , như vậy redux sẽ lưu tất cả state đc định nghĩa vào 1 nơi để bạn sử dụng.

Để sử dụng redux, bạn cần cài nó qua npm. Với rails 5.1, bạn có tùy chọn để sử dụng webpack (từ gem webpacker ) bằng option --webpack .

Khởi tạo

rails new RedditRails --webpack=react -d mysql
(--webpack=react = rails webpacker:install:react = npm install react react-dom)

Project RedditRails đc khởi tạo và sử dụng webpack làm assets bundler, từ đây bạn có thể sử dụng npm hoặc yarn để cài đặt các module. Trỏ đến thư mục gốc của dự án để cài đặt redux và add vào yarn lock:

cd RedditRails/
sudo npm install --save react-redux && sudo yarn add [email protected]^2.0.0

Tạo Index controller và sử dụng React là View chính.

//app/javascript/packs/application.js
console.log('Hello World from Webpacker')

//app/views/index/index.html.erb
<%= javascript_pack_tag "application" %> // application ở thư mục app/javascript/components

Khởi động rails server và webpack bằng 2 terminal riêng biệt rails server./bin/webpack-dev-server

Kết quả: init_result.png

Bắt đầu từ đây, chúng ta sẽ sử dụng ReactJS View chính trong rails.

Các khái niệm cơ bản trong Redux

  1. Actions
    • Actions là payload của data, redux action sẽ gửi data đến reducer để lưu vào store.
    • Actions là JavaScript objects, nó phải có property type để nhận biết action nào đang được thực hiện.
    • Luôn sử dụng action để thay đổi state.
  2. Reducer
    • Actions mô tả những hành động đã xảy ra nhưng nó không thay đổi state khi nhận đc kết quả => sử dụng reducer để thay đổi state.
    • Reducer phải là Pure function
  3. Store
    • Store là object chứa state của toàn bộ ứng dụng.
    • Cho phép lấy state bằng method getState()
    • Update state bằng việc dispatch một action dispatch(action)
    • Subscribe listener qua method subscribe(listener)
    • Handle các listener chưa đăng kí bằng return của subscribe(listener)
    • Chỉ có duy nhất một store trong một ứng dụng redux, nếu bạn muốn quản lý logic cho data hãy sử dụng reducer.
  4. Data flow trong redux:

Như vậy để xử lý data trong redux, đầu tiên bạn cần biết payload của một action là gì, sau đó xử lý logic trong Reducer, phần việc còn lại reducer sẽ lưu các state con vào 1 state cha.

Khai báo redux trong rails

Trong thư mục app/javascript tạo các folder: actions , components, constants , reducers , stores . Mỗi folder sẽ lưu từng file thành phần tương ứng. Như đã nói ở trên, redux luôn sử dụng action để handle state. Vì vậy chúng ta sẽ khởi tạo action trước. Có action thì mới có data, có data thì ta mới dùng reducer xử lý data và lưu vào state đc.

Tạo action
//actions/hello_action.js
export const helloAction = (text) => {
  return {
    type: 'SAY_HELLO',
    payload: text
  }
}

export default helloAction

Tạo reducer
//reducers/hello_reducer.js
export default const helloReducer = (state = [], action) => {
  switch(action.type) {
    case 'SAY_HELLO':
      const { text } = action.payload
      return text
    default:
      return state
  }
}
Tạo demo component
//components/App.js
import React from 'react'
import { connect } from 'react-redux'
import { helloAction } from '../actions/hello_action'
import { bindActionCreators } from 'redux'

class App extends React.Component {
  constructor(props) {
    super(props);
    this.sayHi = this.sayHi.bind(this)
  }

  sayHi(e) {
    const { value } = e.target
    const { helloAction } = this.props
    helloAction(value)
  }

  render() {
    const { text } = this.props
    return(
      <div>
        <h1>Type anything to say hi ^^ </h1>
        <h3>{text || "just a dummy h3"}</h3>
        <input type="text" onChange={(e) => this.sayHi(e)} />
      </div>
    )
  }
}

// lấy text payload từ redux store truyền vào props của App component
const mapStateToProps = state => {
  const { text } = state
  return { text }
}

// lấy ra action helloAction gán vào props của App component
const mapDispatchToProps = dispatch => {
  return {
    helloAction: (text) => {
      dispatch(helloAction(text))
    }
  }
}

// connect component App đến Redux store.
App = connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

export default App


Khai báo redux store
//application.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux';
import { createStore } from 'redux'
import helloReducer from './reducers/hello_reducer';
import App from './components/App'

let store = createStore(helloReducer);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

Kết quả

Như vậy chúng ta vừa tạo ra 1 page đơn giản sử dụng Rails + react-redux. Github của tutorial: https://github.com/nguyenthetoan/RedditRails

Ở bài viết tiếp theo, chúng ta sẽ sử dụng redux với Reddit API.