react 学习总结–Redux、React Redux
说明
redux 版本 :”3.6.0”
react-redux 版本: “^4.4.6”
redux-thunk 版本: “2.1.0”
1.为什么用Redux
React只是 MVC 中的 V 层,并不是完整的Web应用解决方案,如果应用的交互比较多,那么只使用React,会使得代码复杂,不易阅读,Redux使用Flux架构的概念,与函数式编程结合,为React提供状态管理,适用于:多交互、多数据源的应用场景。
2.Redux简介
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。
惟一改变 state 的办法是触发 action,一个描述发生什么的对象。
为了描述 action 如何改变 state 树,你需要编写 reducers。
更多详细介绍请看官方文档
3.Redux三大基础原则
单一数据源
整个应用的state被存储在一棵Object tree 中,他只有一个单一的store
state是只读的
唯一改变state的办法就是触发action,action是一个描述要发生什么的对象
纯函数的形式执行修改
为了描述action如何改变state tree ,你需要编写reducer
4.Action
action 是把数据从应用传到store的有效载荷,他是store数据的唯一来源,在react中,state的变化,会导致view的变化,而state的变化优势view导致的,action就是view发出的通知,表示state需要进行改变
action 是一个对象 type属性是必须的,表示action的名称
const ADD = "ADD";
const action = {
type : ADD,
payload : 'Transfer of data' //其他属性可以用来传递数据
};
action 中传递的数据越少越好
Action Creator
Action Creator 就是生成action的方法,view 要发送多种消息,不一定都要手写,定义一个函数来生成action,会方便很多
const ADD = "ADD";
function actions(msg){
return {
type : ADD,
payload : msg
}
}
const action = actions('msg');
5.Reducer
action 只是描述了有事情发生了这一事实,而 reducer 用于指明应用如何更新 state
reducer是一个纯函数( 纯函数的返回值只由它调用时的参数决定 ,它的执行不依赖于系统的状态),它接受两个参数,第一个参数是旧的state,第二个参数是action,返回值是一个新的action,需要保证:只要传入的参数一样,返回的内容必须一样
let initialState = {
num : 0
}
function reducer(state = initialState,action){
switch (action.type) {
case 'ADD':
return Object.assign({}, state, {num : action.num});
default:
return state;
}
}
注意: 不要修改state,使用
Object.assign()
,第一个参数要为空对象,因为运算后,第一个参数的只会被改变Object.assign() 是 ES6 特性,但多数浏览器并不支持(尤其移动端),所以需要安装插件:
babel-plugin-transform-replace-object-assign
和lodash.assign
,在使用babel编译的时候将其装换成浏览器支持的js
.pipe(babel({
presets : ['es2015', 'react','stage-1'],
"plugins": [
["transform-replace-object-assign", "lodash.assign"]
]
}))
拆分reducer
一个 React 根组件由很多子组件构成,我们可以把子组件和reducer对应起来,然后通过Redux提供的combineReducer方法,将他们合并成一个大的Reducer,这样每个reducer只负责管理全局state中的一部分,每个reducer的state 参数都不同,分别对应它管理的那部分的state数据
import {combineReducers} from 'redux';
const rootReducer = combineReducers({
homeReducer, //当属性名与homeReducer同名时
app : appReducer //属性名与reducer不同名
})
6.Store
store 就是保存数据的地方,同时他也是关联action和reducer的地方,
action 用来描述‘发生了什么’,reducer用来根据action更新state,
store 的具体职责如下
- 维持应用的
state
; - 提供
getState()
方法获取state
; - 提供
dispatch(action)
方法更新state
; - 通过
subscribe(listener)
注册监听器。
store创建
import {createStore} from 'redux';
let store = createStore(reducer);
store.subscribe()
store.subscribe
用于设置监听函数,一旦store中的state改变,就自动执行这个函数
同时这个方法返回一个函数,调用这个函数就会解除监听
let unsubscribe = store.subscribe(() =>
console.log(store.getState()); //打印当前状态
);
unsubscribe(); //解除事件监听
store.dispatch()
store.dispatch
用于 view 发出action ,它接受一个action对象作为参数,将他发送出去
store.dispatch({type:'ADD'}); //直接发出action
store.dispatch(actions(msg)); //结合action creator
store.getState()
获取当前状态
let store = Redux.createStore(reducer);
console.log(store.getState());
7.数据流
严格的单向数据流是 Redux 架构的设计核心。
定义阶段
- 1.设计store中存储数据的格式
- 2.定义发出 action 的名称,以及要携带数据的格式,封装成 action creator
- 3.根据Store中要存储的数据格式和响应 action 的名称,编写reducer,合并到一个reducer中
- 4.创建 store 并传入合并后的reducer,使其在收到action后自动调用reducer
使用阶段
- 1.用户操作引起 view 改变,发出 action
store.dispatch(actions(msg))
- 2.store接收到action,自动调用reducer,并且传入两个参数:当前的state和收到的action(自动完成,不需要手动操作)
- 3.reducer 根据对应的action.type,执行相应的逻辑,最终返回新的state(自动完成,不需要手动操作)
- 4.state 变化 store就会自动调用监听函数,监听函数中可以更改组件的state,从而触发view的重新渲染
下边是完整的使用
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {combineReducers,createStore} from 'redux';
// 定义action creator
function addNumAction(msg){
return {
type : "ADD",
msg : msg
};
}
// 定义reducer
let initialState = {num : 0};
const reducer = (state=initialState,action) => {
switch (action.type) {
case "ADD":
return Object.assign({}, state, {num : state.num + action.msg});
default:
return state;
}
};
const rootReducer = combineReducers({reducer});
// 创建store
let store = createStore(rootReducer);
console.log(store.getState()); //{reducer:{num:0}}
class Test extends Component{
constructor(props){
super(props);
// 调用监听函数,store中的state改变才会触发执行
let unsubscribe = store.subscribe(() => {
let newState = store.getState();
console.log(newState);
this.setState(newState.reducer); //handleClick之后才会调用第一次,不是在此处执行
});
this.state = {
num : store.getState().reducer.num //constructor只执行一次
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
store.dispatch(addNumAction(3));
}
render() {
return (
<div>
<p>{this.state.num}</p>
<button onClick={this.handleClick}>点击+3</button>
</div>
);
}
}
export default connect()(Test);
8.Middleware中间件
Redux middleware,提供了位于 action 发起之后,到达 reducer 之前的扩展,多用于进行一步操作,可以在这里进行日志记录、创建崩溃报告、调用异步接口或者路由接口等
action 发出后 reducer 立即算出state,这叫同步;action 发出后 一段时间在执行reducer,这就是异步
更多中间件与异步操作可以看看大神的博客
applyMiddlewares
applyMiddlewares()
是redux的原生方法,作用是将所有中间件组成一个数组,依次执行
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers/reducer';
const store = createStore(
reducer,
applyMiddleware(thunk)
);
异步操作
与同步操作不同,异步操作需要发出三种 action
- 操作发起 Action
- 操作成功 Action
- 操作失败 Action
有两种action的书写方式,一种是
type
相同 使用status
区分状态,还有一种直接使用不同的type
区分下边是一个完整的写法
/* store */
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers/reducer';
const store = createStore(
reducer,
applyMiddleware(thunk)
);
/* action */
import {REQUEST_START,REQUEST_SUCCESS,REQUEST_ERROR} from '../constants.js';
require('es6-promise').polyfill();
require('isomorphic-fetch');
export function requestBalance() {
return dispatch => {
dispatch({REQUEST_START});
fetch('../../_data/f.json').then(resp => {
if(resp.status === 200){
return resp.json();
}
throw new Error('false of json');
}).then(json => {
dispatch({type:REQUEST_SUCCESS,msg : json});
}).catch(error => {
dispatch({type:REQUEST_ERROR,msg : error});
});
};
}
注意:React本身提供ajax,数据请求需要借助其他库,本示例使用了 fetch API。它是替代 XMLHttpRequest 用来发送网络请求的非常新的 API。由于目前大多数浏览器原生还不支持它,所以需要导入文件:
es6-promise
和isomorphic-fetch
两个库,使用方法见上边,如果需要跨域,还需要引入fetch-jsonp
,然后直接使用fetchJsonp代替fetch就可以了
9.React-Redux
React-Redux
是 Redux 作者封装的一个React专用库,提供了更简洁的API更多内容参考大神的 Redux 入门教程(三):React-Redux 的用法
容器组件和UI组件
容器组件负责管理数据和逻辑,UI组件负责UI呈现,容器组件会与外部通信,并把数据传递给UI组件
明智的做法是只在最顶层组件(如路由操作)里使用 Redux。内部组件应该像木偶一样保持“呆滞”,所有数据都通过 props 传入。容器组件 :最顶层,可以从 Redux 获取 state,也可以向 Redux 发起 actions
UI组件 :子组件, 从 props 获取数据,从 props 调用回调函数
connect()
connect方法,用于将组件与redux绑定起来,从而生成一个容器组件
import React, {Component} from 'react';
import {connect} from 'react-redux';
class Home extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="home-container">
this is home component
</div>
);
}
}
// 利用connect将组件与redux绑定起来
export default connect()(Home);
connect()方法的两个参数
mapStateToProps
和mapDispatchToProps
mapStateToProps :是输入逻辑,定义了外部的state怎么转换为UI组件的props
mapDispatchToProps :是输出逻辑,定义了用户的操作如何变为 action,从UI组件中传出去
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
let mapStateToProps = (state) => {
return {
homeState : state.homeReducer
};
}
let mapDispatchToProps = (dispatch) => {
return {
onhandleClick : bindActionCreators(homeAction,dispatch)
};
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(Home)
组件
React-Redux 提供Provider组件,可以让容器组件拿到state。
Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了
ReactDOM.render((
<Provider store={store}>
<div>
<Router history={browhistory} routes={routes} />
{/* <DevTools /> */}
</div>
</Provider>
), document.getElementById('app'));
相关链接
下一篇–构建工具传送门
- Gulp 安装、使用