在前面三篇 Redux 的教程中已詳細提到 Redux 的實現,大概可能夠總結如下幾點javascript
Reduxcss
Actions
、Reducer
、Store
這三層createStore(reducer)
獲得store
,換名話說store
包含了reducer
的邏輯實現store.dispath(action)
去調用reducer
,從而改變state
store.getState()
獲取在reducer
改變的state
Reacthtml
Component
、state
、props
三大關鍵要素setState()
改變state
從而觸發render
,更新component
react-reduxjava
Provier
和方法connect
connect
將 React 的state
和 Redux 的actions
合併成一個對象props
,再將這個對象和component
生成一個新的組件Provider
負責將 Redux 的store
當屬性傳connect
的新組件,從面將 React 和 Redux 關聯到了一塊兒action
的時候,Provider.store
就會映射調用reducer
從而改變state
,當state
發生改變,就會觸發新組件的render
,從新更新組件。上面是 React 依賴 Redux 的實現過程,但問題來了,若是項目中有異步請求,根據 Redux 的規則是:node
type
的對象state
這樣一來,這個異步請求就變得無處安放了,這個時候的解決方案就是須要一個模塊,在這個模塊中發起 ajax 請求,而後在請求的回調函數中去手動調用reducer
,而這個發起 ajax 請求的模塊就稱之爲中間件react
該案例是以中間件調用 nodejs 的公共接口,實現一個數據列表。git
源碼下載:https://github.com/dk-lan/rea... github
效果預覽web
源碼下載後執行下面步驟例可查看效果ajax
npm install
npm start
| src
|——| components
|——|——| datagrid
|——|——|——| datagridcomponent.js
|——|——|——| datagridaction.js
|——|——|——| datagridconstants.js
|——|——|——| datagridreducer.js
|——|——| cnode
|——|——|——| cnode.js
|——|——| spinner
|——|——|——| spinner.js
|——|——|——| spinner.scss
|——| redux
|——|——| store.js
|——|——| middleware.js
|——|——| rootReducer.js
|——| utils
|——|——| httpclient.js
|——| app.js
import React from 'react' import {connect} from 'react-redux' import SpinnerComponent from '../spinner/spinner' import * as actions from './datagridaction' class DatagridComponent extends React.Component{ getKeys(item){ let cols = item ? Object.keys(item) : []; return this.props.config.cols || cols; } componentWillMount(){ this.props.refresh(this.props.config) } render(){ return ( <div> <table className="table"> <thead> <tr> { this.getKeys(this.props.dataset[0]).map((key) => { return <th key={Math.random()}>{key}</th> }) } </tr> </thead> <tbody> { this.props.dataset.map((item) => { return ( <tr key={item.id || item.indexid} onDoubleClick={this.selectTr.bind(this, item)}> { this.getKeys(item).map((key) => { return <td key={Math.random()}>{item[key]}</td> }) } </tr> ) }) } <tr></tr> </tbody> </table> <SpinnerComponent show={this.props.show}/> </div> ) } } const mapStateToProps = (state) => { return { dataset: state.datagrid.dataset || [], show: state.datagrid.show, error: state.datagrid.error } } export default connect(mapStateToProps, actions)(DatagridComponent);
export const Requesting = 'Requesting' export const Requested = 'Requested' export const RequestError = 'RequestError'
import * as constants from './datagridconstants' export function refresh(_config){ return { types: [constants.Requesting, constants.Requested, constants.RequestError], url: _config.url, method: _config.method || 'get', data: _config.data || {}, name: _config.name } }
import * as constants from './datagridconstants' export default function datagrid(state = {}, action){ let _state = JSON.parse(JSON.stringify(state)); switch(action.type){ case constants.Requesting: _state.show = true; break; case constants.Requested: _state.show = false; if(action.name){ _state[action.name] = _state[action.name] || {}; _state[action.name].dataset = action.result; } else { _state.dataset = action.result; } break; case constants.RequestError: _state.show =false; _state.error = action.error; break } return _state; }
import React, {Component} from 'react' import './spinner.scss' class SpinnerComponent extends React.Component{ render(){ let html = ( <div> <div className="dk-spinner-mask"></div> <div className="dk-spinner dk-spinner-three-bounce"> <div className="dk-bounce1"></div> <div className="dk-bounce2"></div> <div className="dk-bounce3"></div> </div> </div> ) return this.props.show ? html : null; } } export default SpinnerComponent
.dk-spinner-mask { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: #fff; opacity: .4; z-index: 2090; } .dk-spinner-three-bounce.dk-spinner { position: absolute; top: 53%; left: 47%; background-color: none!important; z-index: 2099; margin: 0 auto; width: 70px; text-align: center; } .dk-spinner-three-bounce div { width: 18px; height: 18px; background-color: #1ab394; border-radius: 100%; display: inline-block; -webkit-animation: dk-threeBounceDelay 1.4s infinite ease-in-out; animation: dk-threeBounceDelay 1.4s infinite ease-in-out; -webkit-animation-fill-mode: both; animation-fill-mode: both; } .dk-spinner-three-bounce .dk-bounce1 { -webkit-animation-delay: -.32s; animation-delay: -.32s; } .dk-spinner-three-bounce .dk-bounce2 { -webkit-animation-delay: -.16s; animation-delay: -.16s; } @-webkit-keyframes dk-threeBounceDelay{ 0%, 100%, 80%{-webkit-transform:scale(0); transform:scale(0)} 40%{-webkit-transform:scale(1);transform:scale(1)} } @keyframes dk-threeBounceDelay{ 0%, 100%, 80%{-webkit-transform:scale(0);transform:scale(0)} 40%{-webkit-transform:scale(1);transform:scale(1)} }
import React from 'react' import Datagrid from '../../components/datagrid/datagridcomponent' export default class CNodeComponent extends React.Component{ static defaultProps = { config: { url: 'https://cnodejs.org/api/v1/topics', data: {page: 1, limit: 10}, cols: ['title', 'reply_count', 'top', 'create_at', 'last_reply_at'] } } render(){ return ( <div> <Datagrid config={this.props.config}/> </div> ) } }
import React from 'react' import {combineReducers} from 'redux' import datagrid from '../components/datagrid/datagridreducer' export default combineReducers({ datagrid })
import http from '../utils/httpclient' import * as constants from '../components/datagrid/datagridconstants' export default function(api){ return function(dispatch){ return function(action){ let {type, types, url, data, method = 'get', name} = action; if(!url){ return dispatch(action) } dispatch({type: constants.Requesting}) http[method](url, data).then((res) => { let _action = { type: constants.Requested, name, result: res.data } dispatch(_action) }).catch((error) => { dispatch({type: constants.RequestError}) }) } } }
import React from 'react' import {createStore, applyMiddleware} from 'redux' import rootReducer from './rootReducer' import middleware from './middleware' const store = createStore(rootReducer, applyMiddleware(middleware)); export default store;
import './libs/bootstrap/css/bootstrap.min.css' import './libs/font-awesome/css/font-awesome.min.css' import React from 'react' import ReactDOM from 'react-dom' import {Provider} from 'react-redux' import store from './redux/configStore' import CNodeComponent from './components/cnode/cnode' ReactDOM.render( <Provider store={store}> <CNodeComponent/> </Provider>, document.getElementById('app') )
datagrid
支持動態配置ajax
請求放到了中間件執行ajax
請求爲了統一處理,分爲三個狀態,就是constants.js
文件中的三個變量,分別表明請求前,請求中,請求後spinner
,請求結束後移除加載組件spinner