React Redux Starter Kit
Bài đăng này đã không được cập nhật trong 3 năm
Chắc hẳn các bạn đã quen với react.js và redux. React Redux Starter Kit là một bộ công cụ giúp chúng ta code Front-end với rất nhiều các công nghệ mới. Nó giúp cho chúng ta phát triển, quản lý Front-end một cách dễ dàng hơn. Bắt đầu chính là bước cài đặt và bật server lên để chạy thử. Tất cả đều có tại github. Bây giờ hãy lần lượt làm theo các bước ở đó.
Yêu cầu
- node ^4.5.0
- yarn ^0.17.0 or npm ^3.0.0
Ở đây thì mính sử dụng npm để thực hiện việc demo này
Cài đặt và khởi động
Clone project về
git clone https://github.com/davezuko/react-redux-starter-kit.git <my-project-name>
Khi đã lấy được code về thì việc của các bạn là cài đặt nó
cd <my-project-name>
npm install
Sau đó bật server lên:
npm start
Như các bạn nhìn thấy, dưới đây chình là trang chủ http://localhost:3000/ khi khởi động và chạy ứng dụng.
Dưới đây là trang Counter có sẵn trong project:
Trong đó có 2 chức năng chính là Increment
để tăng Counter lên 1 đơn vị và Double
để nhân đôi Counter.
Giờ chúng ta hãy xem để làm được việc render view và thực hiện 2 hàm đó thì chúng ta cần những gì ở trong project nhé.
Header
Để biết được trang nào được dùng những thành phần nào thì ta thường sẽ tìm ở trong routes.
Với bộ công cụ này thì nó được đặt ở src/routes
. Khi mở thư mục này ra ta sẽ thấy như thế này
├── Counter
│ ├── components
│ │ └── Counter.js
│ ├── containers
│ │ └── CounterContainer.js
│ ├── index.js
│ └── modules
│ └── counter.js
├── Home
│ ├── assets
│ │ └── Duck.jpg
│ ├── components
│ │ ├── HomeView.js
│ │ └── HomeView.scss
│ └── index.js
└── index.js
File src/routes/index.js
là file khai báo các routes của project.
# src/routes/index.js
import CoreLayout from '../layouts/CoreLayout'
import Home from './Home'
import CounterRoute from './Counter'
export const createRoutes = (store) => ({
path : '/',
component : CoreLayout,
indexRoute : Home,
childRoutes : [
CounterRoute(store)
]
})
export default createRoutes
Như các bạn thấy nội dung của file này:
- import các thành phần cần sử dụng
- khai báo indexRoute và các childRoutes
Hầu như cấu trúc nội dung các file js đều như vậy, đều phải import các thành mình muốn dùng vào chứ không có tự động load.
Với mỗi routes con thì đề để trong thư mục của nó và có file index.js
trong đó để định nghĩa. Ví dụ như
# src/routes/Home/index.js
import CoreLayout from '../layouts/CoreLayout'
import Home from './Home'
import CounterRoute from './Counter'
export const createRoutes = (store) => ({
path : '/',
component : CoreLayout,
indexRoute : Home,
childRoutes : [
CounterRoute(store)
]
})
export default createRoutes
# src/routes/Counter/index.js
import { injectReducer } from '../../store/reducers'
export default (store) => ({
path : 'counter',
getComponent (nextState, cb) {
require.ensure([], (require) => {
const Counter = require('./containers/CounterContainer').default
const reducer = require('./modules/counter').default
injectReducer(store, { key: 'counter', reducer })
cb(null, Counter)
}, 'counter')
}
})
Giờ chúng ta nhìn vào thư mục layouts:
# index.js
import CoreLayout from './CoreLayout'
export default CoreLayout
#CorLayout
import React from 'react'
import Header from '../../components/Header'
import './CoreLayout.scss'
import '../../styles/core.scss'
export const CoreLayout = ({ children }) => (
<div className='container text-center'>
<Header />
<div className='core-layout__viewport'>
{children}
</div>
</div>
)
CoreLayout.propTypes = {
children : React.PropTypes.element.isRequired
}
export default CoreLayout
Cũng giống như layout của các framword khác. Ở đây ta thiết lập view default như header, footer ... và render các thành phần con trong một thẻ div
riêng.
<Header />
Dòng này chính là gọi ra Header
đã import ở trên import Header from '../../components/Header'
Khi khởi động server thì hệ thống sẽ load file src/components/Header/index.js
để build. Và khi có thay đổi trong file đó thì hệ thống sẽ build lại nhưng mà bạn xóa nó đi thì hệ thống sẽ lấy config cũ chứ ko load lại file này. Chính vì thế mà khi bạn xóa hay code lỗi ở đó thì sẽ có bug nhưng khi bạn xóa nó đi thì sẽ không ảnh hưởng gì trừ khi bạn khởi động lại server.
Và layout này sẽ được render vào trong div id=root
trong file:
# src/index.html
<!doctype html>
<html lang="en">
<head>
<title>React Redux Starter Kit</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
<div id="root" style="height: 100%"></div>
</body>
</html>
Counter
├── Counter
│ ├── components
│ │ └── Counter.js
│ ├── containers
│ │ └── CounterContainer.js
│ └── modules
│ └── counter.js
│ ├── index.js
# src/routes/Counter/components/Counter.js
import React from 'react'
export const Counter = (props) => (
<div style={{ margin: '0 auto' }} >
<h2>Counter: {props.counter}</h2>
<button className='btn btn-default' onClick={props.increment}>
Increment
</button>
{' '}
<button className='btn btn-default' onClick={props.doubleAsync}>
Double (Async)
</button>
</div>
)
Counter.propTypes = {
counter : React.PropTypes.number.isRequired,
doubleAsync : React.PropTypes.func.isRequired,
increment : React.PropTypes.func.isRequired
}
export default Counter
Đây chính là file bạn định nghĩa cấu trúc của 1 Counter và view để render vào phần {children}
trong CoureLayout
. Như các bạn đã thấy thì ở đây 1 đối tượng Counter có 3 thành phần chính.
- counter: biến có kiểu là number
- doubleAsync: hàm chức năng
- increment: hàm chức năng
Và ở phần view hiển thị sẽ sử dụng những biến và hàm này để thực hiện. Ta xem nội dung ở 2 file sau:
# src/routes/Counter/containers/CounterContainer.js
import { connect } from 'react-redux'
import { increment, doubleAsync } from '../modules/counter'
import Counter from '../components/Counter'
const mapDispatchToProps = {
increment : () => increment(2),
doubleAsync
}
const mapStateToProps = (state) => ({
counter : state.counter
})
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
# src/routes/Counter/modules/counter.js
// ------------------------------------
// Constants
// ------------------------------------
export const COUNTER_INCREMENT = 'COUNTER_INCREMENT'
export const COUNTER_DOUBLE_ASYNC = 'COUNTER_DOUBLE_ASYNC'
// ------------------------------------
// Actions
// ------------------------------------
export function increment (value = 1) {
return {
type : COUNTER_INCREMENT,
payload : value
}
}
export const doubleAsync = () => {
return (dispatch, getState) => {
return new Promise((resolve) => {
setTimeout(() => {
dispatch({
type : COUNTER_DOUBLE_ASYNC,
payload : getState().counter
})
resolve()
}, 200)
})
}
}
export const actions = {
increment,
doubleAsync
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[COUNTER_INCREMENT] : (state, action) => state + action.payload,
[COUNTER_DOUBLE_ASYNC] : (state, action) => state * 2
}
// ------------------------------------
// Reducer
// ------------------------------------
const initialState = 0
export default function counterReducer (state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
Như ta thấy, các hàm increment
và doubleAsync
được định nghĩa trong # src/routes/Counter/modules/counter.js
. Vậy khi trên view gọi nó có gọi vào đó ko?
Câu trả lời là: Có, nhưng nó gọi thông qua connect(mapStateToProps, mapDispatchToProps)(Counter)
trong # src/routes/Counter/containers/CounterContainer.js
. Nó sẽ coi như bạn gửi 1 yêu cầu với 2 tham số là action
và state
.
Khi bạn gọi props.increment
hay props.doubleAsync
thì nó sẽ truy nhập vào
# src/routes/Counter/containers/CounterContainer.js
const mapDispatchToProps = {
increment : () => increment(2),
doubleAsync
}
hay props.counter
là
# src/routes/Counter/containers/CounterContainer.js
const mapStateToProps = (state) => ({
counter : state.counter
})
Biến counter
ở đấy là của props
chứ ko phải là đối tượng Counter
. Giá trị của counter
trong Counter
khác với trong props
.
Ví dụ khi ta thay đổi như sau:
# src/routes/Counter/containers/CounterContainer.js
const mapStateToProps = (state) => ({
counter : state.counter + 3
})
=>
- Nếu
counter
trongCounter
có giá trị là 1 thìcounter
trongprops
có giá trị là 4
=> Trên view sẽ hiển thị là 4 Video demo dưới đây thể hiện rõ điều mình nói ở trên.
Tài liệu tham khảo
[https://github.com/davezuko/react-redux-starter-kit] (https://github.com/davezuko/react-redux-starter-kit)
All rights reserved