React 教程第十四篇 —— Redux 跨组件通信高级篇之中间件

前端之家收集整理的这篇文章主要介绍了React 教程第十四篇 —— Redux 跨组件通信高级篇之中间件前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Redux 复习总结

在前面三篇 Redux 的教程中已详细提到 Redux 的实现,大概可可以总结以下几点

  • Redux

    • ActionsReducerStore这三层
    • 通过createStore(reducer)得到store,换名话说store包含了reducer的逻辑实现
    • 通过store.dispath(action)调用reducer,从而改变state
    • 通过store.getState()获取reducer改变的state
    • Redux 本身与 React 没有并没有半毛线关系
    • @H_502_45@
    • React

      • Componentstateprops三大关键要素
      • 本身通过setState()改变state从而触发render,更新component
      • @H_502_45@
      • react-redux

        • 这是一个第三方模块,它的作用就是本来没有半毛钱关系的 Redux 和 React 关联在一起
        • 它有组件Provier方法connect
        • connect将 React 的state和 Redux 的actions合并成一个对象props,再将这个对象和component生成一个新的组件
        • Provider负责将 Redux 的store属性connect的新组件,从面将 React 和 Redux 关联到了一起
        • 当新组件调用action的时候,Provider.store就会映射调用reducer从而改变state,当state发生改变,就会触发新组件的render,重新更新组件。
        • @H_502_45@ @H_502_45@

          中间件

          上面是 React 依赖 Redux 的实现过程,但问题来了,如果项目中有异步请求,根据 Redux 的规则是:

          • Action 必须返回一个带有属性type的对象
          • Reducer 必须是一个纯函数,负责改变state
          • @H_502_45@

            这样一来,这个异步请求就变得无处安放了,这个时候的解决方案就是需要一个模块,在这个模块中发起 ajax 请求,然后在请求的回调函数中去手动调用reducer,而这个发起 ajax 请求的模块就称之为中间件

            实现

            该案例是以中间件调用 nodejs 的公共接口,实现一个数据列表。

            源码下载:https://github.com/dk-lan/rea...

            效果预览

            源码下载后执行下面步骤例可查看效果

            • npm install
            • npm start
            • @H_502_45@

              结构

              | 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')
              )

              小结

              • 组件 datagrid 支持动态配置
              • ajax请求放到了中间件执行
              • ajax请求为了统一处理,分为三个状态,就是constants.js文件中的三个变量,分别代表请求前,请求中,请求后
              • 请求前显示加载组件spinner,请求结束后移除加载组件spinner
              • @H_502_45@

猜你在找的React相关文章