react+redux框架配置从无到有直到正常运行全流程(下)

写于:2017-1-18

上半段:react+redux框架配置从无到有直到正常运行全流程(上)

开始在项目中使用react和redux

项目的基本目录结构:

项目完整目录

因为把项目上传到git了,所以出现了一些额外的文件,并且项目名字也跟上一篇中的不一样。

注:为什么要命名为a_action,a_component… 只是为了方便,这样这4个文件夹就会排序排在最前面,开发中这4个文件夹是用得最多的


1、配置app.js


配置入口js文件:

import React from 'react'; // react核心,用到jsx的地方,都需要这个
import ReactDOM from 'react-dom';   // 渲染组件时需要
import {Provider} from 'react-redux'; // react和redux连接的桥梁,就是这个Provider
import { Router, browserHistory } from 'react-router'; // 路由组件

// babel本身只能转换ES6语法,但ES6新增的Map、Set、Generator等新功能不会转换,所以需要此插件
import 'babel-polyfill';

// 引入sotre,我们稍后配置
import store from './store';

// 所有的CSS全部引入到入口文件即可
import 'antd/dist/antd.less'; // 这是蚂蚁金服ui框架的样式文件
import './css/css.css'; // 这是我们自定义的css文件

import AppRoutes from './route';    // 所有定义好的路由

// 下面是创建根组件
// 其中引入了store,route,browserHistory
// 这里用的是browserHistory,即路由是依靠url地址的变化跳转的(比如www.test.com/home)
// 也可以使用hashHistory,即路由是依靠锚点的变化跳转的(比如www.test.com/#/home)
ReactDOM.render(
  <Provider store={store}>
    <Router routes={AppRoutes} history={browserHistory} queryKey={false} />
  </Provider>,
  document.getElementById('app-root') //  这个app-root是在index.html中写的div,其id为app-root
);

2、配置store


在src/store下创建index.js,其内容为:

import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';   // 中间件,有了这个就可以支持异步action
import RootReducer from '../a_reducer'; // 所有的reducer

// 创建store
const store = createStore(RootReducer, applyMiddleware(ReduxThunk));

export default store;

3、配置route


在src/route中创建index.js:

import React from 'react'; // react核心
import { Route, Redirect, IndexRedirect } from 'react-router'; // 创建route所需

/* 下面是引入一些我们自己定义的container,作为路由的页面 */
// root这个container很重要 我们稍后配置
import RootContainer from '../a_container/root';
import TestContainer from '../a_container/home'; // 一个主页的container

export default (
  <Route path="/" component={RootContainer}> // 所有的访问,都跳转到rootContainer
    <IndexRedirect to="/home" /> // 默认加载的组件,比如访问www.test.com,会自动跳转到www.test.com/home
    <Route path="/home" component={TestContainer} /> // 一个路由地址,访问www.test.com/home,就会跳转到此
    <Redirect from='*' to='/'  /> // 所有的其他未定义的访问路径,都跳转到根路径,比如访问www.test.com/abc, 但是/abc我们没有定义,就会自动跳转到www.test.com, 而www.test.com又会自动跳转到www.test.com/home
  </Route>
);

4、写一个rootContainer作为所有组件的包裹层


创建src/a_container/root/index.js

import React, { PropTypes as P } from 'react'; // React和ProTypes
import { connect } from 'react-redux'; // connect方法用于创建控制器组件,即数据和行为交由redux管理

/* 需要挂载到redux上的参数 */
const mapStoreStateToProps = (state) => ({
  dispatch: state.dispatch,
});

/* 创建组件 */
class RootContainer extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    // 这个组件是一个包裹组件,所有的路由跳转的页面都会以this.props.children的形式加载到本组件下
    return (
      <div className="boss">
        {this.props.children}
      </div>
    );
  }
}

/* 代码类型检查 */
RootContainer.propTypes = {
  dispatch: P.func,
  children: P.any,
};

export default connect(mapStoreStateToProps)(RootContainer);

5、写一个compont用于测试


创建src/a_compont/test/index.js:

import React, { PropTypes as P } from 'react'; // 引入了React和PropTypes
// PropTypes是用于检查props参数类型,可有可无,最好是有

/* 以类的方式创建一个组件 */
class Com extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }

  /* 组件初始化完毕时触发 */
  componentDidMount() {
  }

  /* 渲染组件 */
  render() {
    return (
      <div>
        <span>{this.props.value}</span>
        <button onClick={() => this.props.onClick(this.props.value)}>点击</button>
      </div>
    );
  }
}

/* 下面是对该组件中涉及到的prop数据进行类型检查,如果类型不匹配会发出警告 */
Com.propTypes = {
  value: P.number,
  onClick: P.func,
};

export default Com;

以上是一个很简单的纯ui组件,渲染后的效果就是页面中有一个span和一个button,button绑定了一个事件,这个事件的实体需要其父级传给它
span中的this.props.value也需要父级传给他。


6、写一个container用于测试


创建src/a_container/home/index.js:


// 所需的各种插件
import React, { PropTypes as P } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';

// 所需的所有组件
import Test from '../../a_component/test'; // 引入第4步创建的组件


// 本页面所需action
import appAction from '../../a_action/app-action'; // 稍后创建

// 最终要交给redux管理的所有变量
const mapStoreStateToProps = (state) => ({
  dispatch: state.dispatch,
  testvalue: state.app.inputvalue,
});


// 最终要交给redux管理的所有action
// 即定义哪些方法将成为action
const mapDispatches = (dispatch) => ({
  fn: {
    onTestAdd: (v) => {
      dispatch(appAction.onTestAdd(v));
    },
  },
});

// 创建组件
class HomePageContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }

  render() {
    return (
      <div>
        <Test
          value={this.props.testvalue}
          onClick={this.props.fn.onTestAdd}
        />
      </div>
    );
  }
}

// ==================
// PropTypes
// ==================

HomePageContainer.propTypes = {
  dispatch: P.func,
  fn: P.object,
  testvalue: P.number,
  location: P.any, // location对象包含了浏览器url中的各种信息,会自动被引入到当前组件中,如果不需要的话可以去掉
};

export default connect(mapStoreStateToProps, mapDispatches)(HomePageContainer);

7、创建一个action


创建src/a_action/app_action.js

export default class AdviserActions {
  // 用户点击按钮时,将触发此方法
  static onTestAdd(num) {
    return { // 这个return,实际上是触发了action,redux会自动去触发reducer中对应的方法
      type: 'TEST::add', // 与reducer中的type对应
      payload: num + 1,
    };
  }
}

8、创建根reducer


因为reducer可以创建很多个,但传给redux的只能是一个,所以创建一个根reduer,将其他所有reducer结合在一起
创建src/a_reducer/index.js

/*
 * 该Reducer为根reducer, 用于结合App中所有的reducer.
 * 由于Redux中必须只有一个store和一个reducer ,
 * 因此使用 combineReducers 来把多个reducer组合在一起
 */

import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';

/* 这里是我们自定义的各种reducer */
import appReducer from './app-reducer'; // 这个稍后创建

/* 利用官方提供的combineReducers将所有reducer组合成一个 */
const RootReducer = combineReducers({
  // 注意一定要加上routing: routerReducer 这是用于redux和react-router的连接
  routing: routerReducer,
  // 其他的reducers
  app: appReducer, // 这里的命名,关系到container中取state对应的reducer的名字
});

export default RootReducer;

9、创建自定义的reducer


创建src/a_reducer/app-reducer.js

const initState = {
  inputvalue: 0, // 初始值
};

/* action 对应的处理方法,用于更新state中的数据 */
const actDefault = (state) => state;

const testAdd = (state, action) => {
  const { payload } = action;
  // 原本初始的时候,inputvalue,这里将最新的payload覆盖原来的值
  return Object.assign({}, state, {
    inputvalue: payload,
  });
};

const reducerFn = (state = initState, action) => {
  switch (action.type) {
  // 匹配type来执行对应的方法,action中返回对应的type,这里就会执行对应的方法
  case 'TEST::add':
    return testAdd(state, action);
  default:
    return actDefault(state, action);
  }
};

export default reducerFn;

如此一来,所有需要的东西都创建好了,形成了一个闭环

①、用户点击页面中的button,
②、button上绑定了点击事件
③、这个点击事件最终执行的是第5步中创建的container中的onTestAdd方法
④、而onTestAdd方法是在第6步中创建的app-action.js中定义的
⑤、这个方法把参数的值+1后发出一个action,redux会自动去调用reducer
⑥、这个action的type是TEST::add,reducer中有一个对应的type
⑦、所以那个对应的type所对应的方法被执行,把最新的值覆盖了原来state中的值,这样值就被改变了
⑧、react会自动去重新渲染页面,所以看到页面中的值被加了1


到此为止,配置了一个最基本的react+redux框架,在开发中需要写大量的组件,大量的逻辑。
这套体系的优点在于:
①、组件复用
②、专注于数据,只需要关心数据的变化,不用去考虑事件触发等

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页