一、为什么使用redux?
- React本身只留给我们state和props来管理应用数据的状态,context只是实验性质的一种传递数据的方式。也就是说,React本身并没有很好地提供应用状态管理的解决方案,在一些小应用中我们可能感觉不到状态管理是一个问题,但是随着应用的复杂度增加,这个问题会暴露得越来越明显。这时候我们就需要使用到Redux。
- 既然React推荐我们尽量少得编写有状态组件,那么我们何不干脆把一个应用的数据状态统一在一个地方管理?这便是Redux的理念。
- Redux把应用的数据统一存储在一个全局对象当中。把应用所有的数据交互和状态改变,统一用固定形式的action动作对象来描述,经由名为reducer的方法来判断不同的动作来如何改变应用状态,最后我们通过Store对象来负责执行action动作,获取state应用状态,订阅状态改变时的触发的事件。
- 事实上redux只是提供了一种应用状态管理问题的解决思路,它的原码也是比较少的,我们甚至可以使用一些基本的函数来方法来模拟实现redux 的全部功能,即使我们在应用中使用了redux,编写的大部分也都是原生JS函数和对象,也就是说我们只需要理解redux 的理念,甚至不需要使用redux这个库本身,也能通过他的方式编写代码解决问题。
二、redux的主要功能以及如何使用?
1. redux的主要功能:
- store里记录所有的状态(state);
- 需要改变状态的时候,告诉专员(dispatch)要做什么(action);
- 处理的人(reducer)拿到state和action生成新的state。
2. 使用方法:
- 通过reducer新建store,随时通过store.getState()获取状态;
- 当需要变更状态的时候,通过store.dispatch(action)来修改状态;
- reducer函数接收state和action,返回新的state,可以store.subscrible()监听每次修改。
3. redux的基本使用:
index.js
import { createStore } from 'redux';
//新建reducer,根据老的state和action,生成新的state
const counter= (state = 0,action) => {
switch(action.type){
case 'ADD' :
return state + 1 ;
case 'REMOVE' :
return state - 1;
default:
return state;
}
}
//新建store
const store = createStore(counter);
const init = store.getState();
console.log(init);//0
const listener = () => {
const current = store.getState();
console.log(current);
}
//通过subscribe订阅事件,每次dispatch的时候会自动触发listener事件
store.subscribe(listener);
//dispatch派发事件,传递一个action
store.dispatch({type:'ADD'});//1
store.dispatch({type:'REMOVE'});//0
注意:
- store.dispatch()方法可以传递给组件,内部可以调用修改状态;
- subscribe()方法添加一个变化监听器。每当dispatch action 的时候就会执行,state 树中的一部分可能已经变化。我们可以在回调函数里调用来拿到当前 state。subscribe()方法订阅render函数,每次修改状态都会重新渲染。
4. react和redux一起使用:
- a. 首先新建一个index.redux.js文件夹,把与redux 相关的内容单独移到这个文件夹下;
- b. 然后删除index.js下的代码,进行修改,
- 新建一个App.js。
index.js
import React from 'react';
import ReactDom from 'react-dom';
import { createStore } from 'redux';
import App from './App';
import { counter } from './index.redux';
const store = createStore(counter)
function render(){
ReactDom.render(
<App store={store} />,
document.getElementById('root')
)
}
render();
store.subscribe(render);
App.js
import React, { Component } from 'react';
import { add,remove } from './index.redux';
class App extends Component {
render(){
const store = this.props.store;
const num = store.getState();
return(
<div>
<p>{ `数值为:${num}` }</p>
<button onClick={ () => store.dispatch(add())}>加1</button>
<button onClick={ () => store.dispatch(remove())}>减1</button>
</div>
)
}
}
export default App;
index.redux.js
//action 常量
const ADD = 'ADD';
const REMOVE = 'REMOVE';
//reducer
//新建reducer,根据老的state和action,生成新的state
export const counter= (state = 0,action) => {
switch(action.type){
case ADD :
return state + 1 ;
case REMOVE :
return state - 1;
default:
return state;
}
}
//action creator
export const add = () => {
return {
type:ADD
}
}
export const remove = () => {
return {
type:REMOVE
}
}
5. redux-thunk异步处理action:
- redux异步处理,使用applyMiddleware开启thunk中间件,这时action可以返回函数值,使用dispatch提交action
- 主要对上面的代码进行了解耦,以及来使用redux-thunk,applyMiddleware来处理异步action
index.js
import React from 'react';
import ReactDom from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import App from './App';
import { counter, add, remove,addAsync } from './index.redux';
const store = createStore(counter,applyMiddleware(thunk))
function render(){
ReactDom.render(
<App store={store} add={add} remove={remove} addAsync={addAsync}/>,
document.getElementById('root')
)
}
render();
store.subscribe(render);
App.js
import React, { Component } from 'react';
//import { add, remove } from './index.redux';
class App extends Component {
render(){
const store = this.props.store;
const add = this.props.add;
const remove = this.props.remove;
const addAsync = this.props.addAsync;
const num = store.getState();
return(
<div>
<p>{ `数值为:${num}` }</p>
<button onClick={ () => store.dispatch(add())}>加1</button>
<button onClick={ () => store.dispatch(remove())}>减1</button>
<button onClick={ () => store.dispatch(addAsync())}>异步2秒后加1</button>
</div>
)
}
}
export default App;
index.redux.js
//action 常量
const ADD = 'ADD';
const REMOVE = 'REMOVE';
//reducer
//新建reducer,根据老的state和action,生成新的state
export const counter= (state = 0,action) => {
switch(action.type){
case ADD :
return state + 1 ;
case REMOVE :
return state - 1;
default:
return state;
}
}
//action creator
export const add = () => {
return {
type:ADD
}
}
export const remove = () => {
return {
type:REMOVE
}
}
//使用applyMiddleware 来处理异步action
export const addAsync = () => {
//thunk 插件的使用,这里可以返回函数
return dispatch => {
setTimeout(() => {
//异步结束后,手动执行dispatch
dispatch(add());
},2000)
}
}
6. 下面使用react-redux:
- 在使用react-redux的时候我们可以忘记subscribe,只需要记住reducer,action和dispatch即可;
- react-redux提供了provider和connect两个接口;
- provider组件在应用的最外层传入store即可,只用一次;
- connect负责从外部获取组件所需要的参数;
index.js
import React from 'react';
import ReactDom from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import App from './App';
import { counter } from './index.redux';
const store = createStore(counter,compose(
applyMiddleware(thunk),
window.devToolsExtension()
));
ReactDom.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
)
App.js
import React, { Component } from 'react';
//import { add, remove } from './index.redux';
import { connect } from 'react-redux';
import { add, remove, addAsync } from './index.redux';
class App extends Component {
render(){
/*const store = this.props.store;
const add = this.props.add;
const remove = this.props.remove;
const addAsync = this.props.addAsync;
const num = store.getState();*/
//const { num, add, remove, addAsync } = this.props;
const num = this.props.num;
const add = this.props.add;
const remove = this.props.remove;
const addAsync = this.props.addAsync;
return(
<div>
<p>{ `数值为:${num}` }</p>
<button onClick={ add }>加1</button>
<button onClick={ remove }>减1</button>
<button onClick={ addAsync }>异步2秒后加1</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
num : state
}
}
const mapDispatchToProps = { add, remove, addAsync };
export default connect(mapStateToProps,mapDispatchToProps)(App);
index.redux.js
//action 常量
const ADD = 'ADD';
const REMOVE = 'REMOVE';
//reducer
//新建reducer,根据老的state和action,生成新的state
export const counter= (state = 0,action) => {
switch(action.type){
case ADD :
return state + 1 ;
case REMOVE :
return state - 1;
default:
return state;
}
}
//action creator
export const add = () => {
return {
type:ADD
}
}
export const remove = () => {
return {
type:REMOVE
}
}
//使用applyMiddleware 来处理异步action
export const addAsync = () => {
//thunk 插件的使用,这里可以返回函数
return dispatch => {
setTimeout(() => {
//异步结束后,手动执行dispatch
dispatch(add());
},2000)
}
}
三、模拟实现redux:
模拟实现createStore:
export const createStore = (reducer) => {
//当前的内部状态currentState,刚开始为空
let currentState = {};
//监听器数组
let currentListeners = [];
//getState函数获取当前应用的状态并返回
const getState = () => {
return currentState;
}
//订阅事件函数,传递一个listener监听器
const subscribe = (listener) => {
//把当前的监听器push到监听器数组currentListeners内
currentListeners.push(listener);
}
//dispatch事件函数,传入action
const dispatch = (action) => {
/* 调用传递进来的reducer函数,把当前的内部状态currentState和action
* 传入reducer,并且把reducer调用后的结果赋值给当前的状态currentState
*/
currentState = reducer(currentState,action);
//在dispatch的时候,执行一下每一个监听器函数
currentListeners.forEach( v =>v() );
//返回action
return action;
}
//在初始化的时候执行一个默认的action
dispatch({type:'@@redux/INIT'})
//最后返回getState,subscribe,dispatch三个函数
return { getState, subscribe, dispatch };
}
模拟实现Provider:
export class Provider extends React.Component{
static childContextTypes = {
store: PropTypes.object
}
getChildContext(){
return {store:this.store}
}
constructor(props, context){
super(props, context)
this.store = props.store
}
render(){
return this.props.children
}
}
模拟实现connect:
export const connect = (mapStateToProps=state=>state,mapDispatchToProps={}) =>(WrapComponent)=>{
return class ConnectComponent extends React.Component {
static contextTypes = {
store:PropTypes.object
}
constructor(props,context){
super(props,context);
this.state = {
props:{}
}
}
componentDidMount(){
//从Provider中用context获取store
const { store } = this.context;
//每当dispa的时候,调用一次this.update()更新一下props
store.subscribe(()=>this.update())
//页面初识化的时候,更新一下props
this.update();
}
//获取mapStateToProps和mapDispatchToProps放入this.props里
update(){
//从Provider中用context获取store
const { store } = this.context;
//connect内部执行mapStateToProps返回state
const stateProps = mapStateToProps(store.getState());
//方法需要dispatch,bindActionCreators把actionCreator用dispatch包了一层
const dispatchProps = bindActionCreators(mapDispatchToProps,store.dispatch)
this.setState({
props:{
...this.state.props,
...stateProps,//整合本身的props和stae里面获取到的props,也就是把state放入到prop里面
...dispatchProps//把action放入里面
}
})
}
render(){
return <WrapComponent {...this.state.props}></WrapComponent>
}
}
}
模拟实现bindActionCreators:
//绑定creator和dispatch
//相当于dispatch(actionCreator())返回state
const bindActionCreator = (creator,dispatch) => {
//返回一个用dispatch包了一层的actionCreator
return (...args) => dispatch(creator(...args))
}
//传入actionCreators,dispatch
export const bindActionCreators = (creators,dispatch) => {
let bound = {};
Object.keys(creators).forEach((v) => {
//得到每一个actionCreator
let creator = creators[v]
bound[v] = bindActionCreator(creator,dispatch)
})
return bound;
/*return Object.keys(creators).reduce((ret,item) => {
ret[item] = bindActionCreator(creators[item],dispatch);
return ret;
},{})*/
}