写于: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框架,在开发中需要写大量的组件,大量的逻辑。 这套体系的优点在于: ①、组件复用 ②、专注于数据,只需要关心数据的变化,不用去考虑事件触发等