Redux 复习总结
在前面三篇 Redux 的教程中已详细提到 Redux 的实现,大概可可以总结以下几点
-
Redux
-
React
- 有
Component
、state
、props
三大关键要素 - 本身通过
setState()
改变state
从而触发render
,更新component
- 有
-
react-redux
中间件
上面是 React 依赖 Redux 的实现过程,但问题来了,如果项目中有异步请求,根据 Redux 的规则是:
这样一来,这个异步请求就变得无处安放了,这个时候的解决方案就是需要一个模块,在这个模块中发起 ajax 请求,然后在请求的回调函数中去手动调用reducer
,而这个发起 ajax 请求的模块就称之为中间件
实现
该案例是以中间件调用 nodejs 的公共接口,实现一个数据列表。
源码下载:https://github.com/dk-lan/rea...
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
datagridcomponent.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);
datagridconstants.js
- export const Requesting = 'Requesting'
- export const Requested = 'Requested'
- export const RequestError = 'RequestError'
datagridaction.js
- 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
- }
- }
datagridreducer.js
- 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;
- }
spinner.js
- 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
spinner.scss
- .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%,80%{-webkit-transform:scale(0);transform:scale(0)}
- 40%{-webkit-transform:scale(1);transform:scale(1)}
- }
cnode.js
- 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>
- )
- }
- }
rootReducer.js
- import React from 'react'
- import {combineReducers} from 'redux'
- import datagrid from '../components/datagrid/datagridreducer'
- export default combineReducers({
- datagrid
- })
middleware.js
- 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})
- })
- }
- }
- }
store.js
- 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;
app.js
- 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')
- )