redux单独使用:
import {createStore} from './redux';
const ADD = 'ADD';
const MINUS = 'MINUS';
let initialState=0;
function reducer(oldState,action){
switch(action.type){
case ADD:
return oldState+1;
case MINUS:
return oldState-1;
default:
return oldState;
}
}
let store = createStore(reducer,initialState);
let counterValue = document.getElementById('counter-value');
let addBtn = document.getElementById('add-btn');
let minusBtn = document.getElementById('minus-btn');
function render(){
counterValue.innerHTML = store.getState();
}
render();
store.subscribe(render);
addBtn.addEventListener('click',()=>store.dispatch({type:ADD}));
minusBtn.addEventListener('click',()=>store.dispatch({type:MINUS}));
function createStore(reducer,preloadedState){
let state = preloadedState;
let listeners = [];
function getState(){
return state;
}
function dispatch(action){
//根据老状态和action动作,计算新状态
state = reducer(state,action);
listeners.forEach(l=>l());
}
function subscribe(listener){
listeners.push(listener);
}
return {
getState,//用来获取当前的仓库中的状态
dispatch,//向仓库派发动作
subscribe,//用来订阅仓库中的状态的变化
}
}
export default createStore;
bindActionCreators其实就是把页面用到的action放到了一起管理,实际使用场景不太多
使用及实现的案例:
let actions = {
add(){
return {type:ADD};
},
minus(){
return {type:MINUS};
}
}
const intialState = {number:0};
function reducer(state=intialState,action){
switch(action.type){
case ADD:
return {number:state.number+1};
case MINUS:
return {number:state.number-1};
default:
return state;
}
}
let store = createStore(reducer);
//let boundAdd = bindActionCreators(add,store.dispatch);
//let boundMinus = bindActionCreators(minus,store.dispatch);
let boundActions = bindActionCreators(actions,store.dispatch);
class Counter1 extends Component {
state = {number:store.getState().number}
componentDidMount(){
this.unsubscribe = store.subscribe(()=>{
this.setState({number:store.getState().number});
});
}
componentWillUnmount(){
this.unsubscribe();
}
render() {
return (
<div>
<p>{store.getState().number}</p>
<button onClick={boundActions.add}>+</button>
<button onClick={boundActions.minus}>-</button>
</div>
)
}
}
export default Counter1;
function bindActionCreator(actionCreator,dispatch){
return function(...args){
return dispatch(actionCreator.apply(this,args));
}
}
/**
*
* @param {*} actionCreators action的创建者 此处可以只传一个创建者,也就是一个函数,也可以传一个对象
* @param {*} dispatch
*/
function bindActionCreators(actionCreators,dispatch){
if(typeof actionCreators === 'function'){
return bindActionCreator(actionCreators,dispatch)
}
const boundActionCreators = {};
for(const key in actionCreators){
const actionCreator = actionCreators[key];
boundActionCreators[key]=bindActionCreator(actionCreator,dispatch)
}
return boundActionCreators;
}
export default bindActionCreators;
redux的源码中,createStore中还会默认调用一次dispatch:dispatch({type:'@@REDXU/INIT'});
function createStore(reducer,preloadedState){
console.log('exec createStore')
let state = preloadedState;
let listeners = [];
function getState(){
return state;
}
function dispatch(action){
//根据老状态和action动作,计算新状态
state = reducer(state,action);
listeners.forEach(l=>l());
}
function subscribe(listener){
listeners.push(listener);
}
dispatch({type:'@@REDXU/INIT'});
return {
getState,//用来获取当前的仓库中的状态
dispatch,//向仓库派发动作
subscribe,//用来订阅仓库中的状态的变化
}
}
export default createStore;
以Counter组件为例,代码执行的顺序为:
1、createStore(reducer)
2、createStore(reducer) -> dispatch({type:'@@REDXU/INIT'})
3、dispatch({type:'@@REDXU/INIT'}) -> reducer(state,action)
4、第3步中的reducer的第1个参数state依次从preloadedState intialState将初始值接管过来
5、这个type值为'@@REDXU/INIT'的action在reducer中没有匹配到任何的action,所以会走最后的default,即直接return state
6、用新的值执行listener监听
无论项目多大,store只维护一棵大的state树,项目开发中通常会按照业务将state分为若干类,所以需要把所有的state整合在一起
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Counter1 from './components/Counter1';
import Counter2 from './components/Counter2';
ReactDOM.render(<><Counter1/><Counter2/></>,document.getElementById('root'));
=====================================
// Counter1.jsx
import React, { Component } from 'react'
import {bindActionCreators} from 'zredux';
import store from '../store';
import actions from '../store/actions/counter1';
console.log('import Counter1')
let boundActions = bindActionCreators(actions,store.dispatch);
class Counter1 extends Component {
=====================================
// store/index.js
import {createStore} from 'zredux';
import rootReducer from './reducers';
console.log('import store index')
let store = createStore(rootReducer);
export default store;
=====================================
// store/reducers/index.js
import {combineReducers} from 'zredux';
import counter1 from './counter1';
import counter2 from './counter2';
console.log('import combineReducers index')
let rootReducer = combineReducers({
counter1,
counter2
});
export default rootReducer;
=====================================
// store/reducers/counter1
import * as actionTypes from '../action-types'
//Counter1组件对应的state
console.log('import counter1 reducers')
let intialState = {number:0,color:'black'};
//Counter1组件对应的reducer
function counter1(state=intialState,action){
switch(action.type){
case actionTypes.ADD1:
return {...state,number:state.number+1};
case actionTypes.MINUS1:
return {...state,number:state.number-1};
case actionTypes.CHANGE_COLOR:
return {...state,color:action.payload};
default:
return state;
}
}
export default counter1
console.log('import combineReducers')
function combineReducers(reducers){
/**
* state 老的总状态
* action 动作
*/
return function(state={},action){
let nextState = {};
//reducers = {counter1,counter2}
for(let key in reducers){
//nextState.counter1 = counter1(oldCounter1State,action);
nextState[key]=reducers[key](state[key],action);
}
return nextState;
}
}
export default combineReducers;
当在文件中引用自己写的redux时经常需要将import {createStore} from 'redux'这种代码改为import {createStore} from '../redux',这种改动比较繁琐,所以我们可以将createStore等文件放到一个自定义目录下,例如zindex,然后在jsconfig.json中添加配置:
{
"compilerOptions": {
"baseUrl": "src"
}
}
这样就可以通过import {createStore} from 'zredux'引用了
以上述代码为例,我们分析一下store整体的执行流程:
- createStore.js:6 import createStore
- combineReducers.js:1 import combineReducers
- counter1.js:4 import counter1 reducers
- counter2.js:4 import counter2 reducers
- index.js:4 import combineReducers index
- combineReducers.js:3 exec combineReducers
- index.js:3 import store index
- createStore.js:8 exec createStore
- Counter1.js:5 import Counter1
- bindActionCreators.js:17 exec bindActionCreators
- Counter2.js:5 import Counter2
- bindActionCreators.js:17 exec bindActionCreators
- Counter1.js:10 register subscribe
可以看到,js会递归import(1-5步),之后在第6步中,执行combineReducers,也就是这段代码的执行:
let rootReducer = combineReducers({
counter1,
counter2
});
执行之后,会返回一个函数:
closure: reducers
rootReducer === function(state={},action){
let nextState = {};
//reducers = {counter1,counter2}
for(let key in reducers){
//nextState.counter1 = counter1(oldCounter1State,action);
nextState[key]=reducers[key](state[key],action);
}
return nextState;
}
这个rootReducer将会作为参数传入createStore中创建出store对象来
创建store的过程中通过dispatch({type:'@@REDXU/INIT'})初始化,再执行rootReducer方法,即:
rootReducer(preloadedState, {type:'@@REDXU/INIT'})
preloadedState在此处是undefined,所以state默认为空对象
这个函数在执行时,闭包中存着所有的reducers,结构为:
{
closure intialState = {number:0,color:'black'};
counter1: function (state=intialState,action){
switch(action.type){
case actionTypes.ADD1:
return {...state,number:state.number+1};
case actionTypes.MINUS1:
return {...state,number:state.number-1};
case actionTypes.CHANGE_COLOR:
return {...state,color:action.payload};
default:
return state;
}
},
closure intialState = {number:0,color:'black'};
counter2: function (state=intialState,action){
switch(action.type){
case actionTypes.ADD2:
return {...state,number:state.number+1};
case actionTypes.MINUS2:
return {...state,number:state.number-1};
case actionTypes.CHANGE_COLOR:
return {...state,color:action.payload};
default:
return state;
}
},
}
这几个reducer将会依次执行,执行时由于state['counter1']和state['counter2']都是undefined,所以默认会赋值为initialState
又由于'@@REDXU/INIT'这种类型的action不匹配任何actionType,所以都走default,直接将state返回
所以本次rootReducer执行后返回的结果为:
{
counter1: {number:0,color:'black'},
counter2: {number:0,color:'black'},
}
dispatch中接到这个state,将其赋值给闭包state:
closure state = preloadedState;
function dispatch(action){
//根据老状态和action动作,计算新状态
state = reducer(state,action);
listeners.forEach(l=>l());
}
然后监听函数listeners就会挨个执行