亲宝软件园·资讯

展开

React redux 原理及使用详解

空山与新雨 人气:0

概述

整个工作流程:

三大原则

1.单一数据源(Store) 整个应用的State被存放在一棵Object tree中,并且这个Object tree只存在唯一一个Store中;

2.State是只读的 唯一改变 State 的方法就是触发 Action,Action 是一个用于描述已发生事件的普通对象。 确保了所有的修改都能被集中化处理。

3.通过纯函数Reducer来修改Store, Reducer 只是一些纯函数,它接收先前的 State 和 Action,并返回新的 State。 即reducer(state, action) => new state

createStore创建store

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    // 第二个参数是一个函数,没有第三个参数的情况
    enhancer = preloadedState;
    preloadedState = undefined;
  }
  // 如果第三个参数是函数走下面的逻辑,返回一个新的createStore
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      // enhancer 不是函数就报错
      throw new Error('Expected the enhancer to be a function.');
    }
    // enhancer就是高阶函数,强化了本身这个createStore的函数,拿到增强后的createStore函数去处理
    // applyMiddleware这个函数还会涉及到这个
    return enhancer(createStore)(reducer, preloadedState);
  }
  if (typeof reducer !== 'function') {
    // reducer不是函数报错
    throw new Error('Expected the reducer to be a function.');
  }
  // 其他代码省略
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable,
  };
}

applyMiddleware 应用中间件

export default function applyMiddleware(...middlewares) {
  return (createStore) =>
    (...args) => {
      const store = createStore(...args);
      let dispatch = () => {
        throw new Error(
          'Dispatching while constructing your middleware is not allowed. ' +
            'Other middleware would not be applied to this dispatch.',
        );
      };
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args),
      };
      const chain = middlewares.map((middleware) => middleware(middlewareAPI));
      dispatch = compose(...chain)(store.dispatch);
      return {
        ...store,
        dispatch,
      };
    };
}
// 其实就是修改了dispatch
let store = applyMiddleware(middleware1,middleware2)(createStore)(rootReducer);

combineReducers 合并多个reducer

从执行结果看,这时候 state 已经变成了一个以这些 reducer 为 key 的对象;

reducer 也变成了一个合并的 reducer;

遍历执行所有的 reducer 的时候把 action 传进去,返回新的 state;

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers);
  const finalReducers = {};
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i];
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key];
    }
  }
  const finalReducerKeys = Object.keys(finalReducers);
  /* 返回一个整合后的reducers */
  return function combination(state = {}, action) {
    let hasChanged = false;
    const nextState = {};
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i];
      const reducer = finalReducers[key];
      const previousStateForKey = state[key];
      const nextStateForKey = reducer(previousStateForKey, action);
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action);
        throw new Error(errorMessage);
      }
      nextState[key] = nextStateForKey;
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
    }
    return hasChanged ? nextState : state;
  };
}

dispatch

function dispatch(action) {
  if (!isPlainObject(action)) {
    throw new Error(
      'Actions must be plain objects. ' +
        'Use custom middleware for async actions.',
    );
  }
  if (typeof action.type === 'undefined') {
    throw new Error(
      'Actions may not have an undefined "type" property. ' +
        'Have you misspelled a constant?',
    );
  }
  if (isDispatching) {
    throw new Error('Reducers may not dispatch actions.');
  }
  try {
    isDispatching = true;
    currentState = currentReducer(currentState, action);
  } finally {
    isDispatching = false;
  }
  var listeners = (currentListeners = nextListeners);
  for (var i = 0; i < listeners.length; i++) {
    var listener = listeners[i];
    listener();
  }
  return action;
}

中间件

function logger(store) {
  return function (next) {
    return function (action) { // 新的 dispatch 函数
      console.group(action.type);
      console.info('dispatching', action);
      let result = next(action);
      console.log('next state', store.getState());
      console.groupEnd();
      return result;
    };
  };
}

中间件的调用顺序

import React from 'react';
import { createStore, applyMiddleware } from 'redux';
function createLogger({ getState, dispatch }) {
  return (next) => (action) => {
    const prevState = getState();
    console.log('createLogger1');
    const returnValue = next(action);
    const nextState = getState();
    const actionType = String(action.type);
    const message = `action ${actionType}`;
    console.log(`%c prev state`, `color: #9E9E9E`, prevState);
    console.log(`%c action`, `color: #03A9F4`, action);
    console.log(`%c next state`, `color: #4CAF50`, nextState);
    return returnValue;
  };
}
function createLogger2({ getState }) {
  return (next) => (action) => {
    const console = window.console;
    const prevState = getState();
    console.log('createLogger2');
    const returnValue = next(action);
    const nextState = getState();
    const actionType = String(action.type);
    const message = `action ${actionType}`;
    console.log(`%c prev state2`, `color: #9E9E9E`, prevState);
    console.log(`%c action2`, `color: #03A9F4`, action);
    console.log(`%c next state2`, `color: #4CAF50`, nextState);
    return returnValue;
  };
}
const reducer = function (state = { number: 0 }, action) {
  switch (action.type) {
    case 'add':
      return {
        number: state.number + action.number,
      };
    default:
      return state;
  }
};
const store = createStore(
  reducer,
  applyMiddleware(createLogger, createLogger2),
);
store.subscribe(function () {
  console.log(1111);
});
const { dispatch } = store;
const App = () => {
  const handler = () => {
    dispatch({ type: 'add', number: 10 });
  };
  return (
    <div>
      <button onClick={handler}>触发redux</button>
    </div>
  );
};
export default App;

store

store 的属性如下:

dispatch: ƒ dispatch(action)

getState: ƒ getState()

replaceReducer: ƒ replaceReducer(nextReducer)

subscribe: ƒ subscribe(listener)

redux 数据流

Redux 的数据流是这样的:

界面 => action => reducer => store => react => virtual dom => 界面

bindActionCreators

将action对象转为一个带dispatch的方法

比如connect接收的mapDispatchToProps 是对象,会使用 bindActionCreators 处理; 接收 actionCreator 和 dispatch,返回一个函数;

function bindActionCreator(actionCreator, dispatch) {
  // 返回一个函数
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}
function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}
const mapDispatchToProps = {  // actionCreators 这是个集合,
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
      filter: filter
  };
}
转换为:
const mapDispatchToProps = {  // actionCreators 这是个集合,
  onClick:function(filter) {
    return dispatch({  // dispatch 是闭包中的方法
      type: 'SET_VISIBILITY_FILTER',
      filter: filter
    })
  }
}

compose

函数套函数,compose(...chain)(store.dispatch)结果返回一个加强了的 dispatch;

这点和koa比较相似,这个 dispatch 在执行的时候会调用中间件。

function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  // 每一次reduce迭代都会返回一个加强版的dispatch
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args)),
  );
}

加强版 dispatch(一个方法,接收 action 参数),在中间件中用 next 表示,执行 next 之后,会形成一个链条。

enhancer

// 以createStore为参数
(createStore) =>
  (...args) => {};

使用 redux 常遇见的问题

加载全部内容

相关教程
猜你喜欢
用户评论