1. 90前端首页
  2. 前端开发
  3. JavaScript

redux原理

Redux is a predictable state container for JavaScript apps. redux借鉴了函数编程的思想,采用了单向数据流的理念。用户不能直接改变store,只能通过派发action来更新store,做到了数据的可追溯。本片文章一步步实现redux,实现对redux原理的理解。

createStore的实现

首先createStore是使用方式是:

const store = createStore(reducer);
// 获取完整的store对象
store.getStore();
// 派发更新,dispatch接收一个action,会更新store
store.dispatch(actions)
// 监听store改变,store改变时调用listner
store.subscribe(listner);

完整的实现方式为:

const createStore = (reducer) => {
  let store = {};
  let listners = [];

  const getStore = () => store;

  const dispatch = (action) => {
    store = reducer(store, action);
    listners.forEach(item => item())
  }

  const subscribe = (listner) => {
    listners.push(listner);
    return () => {
      // 可以取消订阅
      listners = listners.filter(item => item !== listner);
    }
  }

  return {
    getStore,
    dispatch,
    subscribe
  }
}

上述实现其实就是一个简单的发布订阅者设计模式。使用示例:

const reducers = (state = { counter: 0 }, action) => {
  switch (action.type) {
    case \'INCREMENT\':
        return {...state, counter: state.counter + action.payload};
    case \'DECREASE\':
       return {...state, counter: state.counter - action.payload};
    default:
    return state;
  }
}

// 创建
const store = createStore(reducers);
// 订阅
const subscribe1 = store.subscribe(() => {
  console.log(\'--订阅1--\');
  console.log(store.getStore().counter)
});
// 订阅
const subscribe2 = store.subscribe(() => {
  console.log(\'--订阅2--\');
  console.log(store.getStore().counter)
});
// 派发更新,输出订阅1和订阅2
store.dispatch({
  type: \'INCREMENT\',
  payload: 2
})
// 取消订阅
subscribe2();
// 派发更新,只输出订阅1
store.dispatch({
  type: \'DECREASE\',
  payload: 1
})

实现日志记录

实现能够在dispatch前后打印出来store的信息。

// 创建
const store = createStore(reducers);
// 拦截旧的dispatch,在其调用前后插入日志代码
const logger = (store) => {
  const oldDispatch = store.dispatch;
  return (action) => {
    console.log(\'--更新前--\');
    console.log(store.getStore());
    oldDispatch(action);
  }
}

store.dispatch = logger(store);

store.dispatch({
  type: \'INCREMENT\',
  payload: 2
})

middlewares

假设还有一个logger2打印更新后的日志,调用方法就是

store.dispatch = logger(store);
store.dispatch = logger2(store);

实现

const appliMiddlewares = (store, middlewares) => {
    middlewares.forEach(middleware => {
        store.dispatch = middleware(store)(store.dispatch)
    })
}

那么改写原来的logger实现为

// 创建
const store = createStore(reducers);
// 拦截旧的dispatch,在其调用前后插入日志代码。
const logger = (store) => (oldDispatch) => {
  return (action) => {
    console.log(\'--更新前--\');
    console.log(store.getStore());
    const returnStore = oldDispatch(action);
    return returnStore;
  }
}
// 拦截旧的dispatch,在其调用前后插入日志代码
const logger1 = (store) => (oldDispatch) => {
  return (action) => {
    const returnStore = oldDispatch(action);
    console.log(\'--更新后--\');
    console.log(store.getStore());
    return returnStore;
  }
}

const appliMiddlewares = (store, middlewares) => {
  middlewares.forEach(middleware => {
      store.dispatch = middleware(store)(store.dispatch)
  });
}

appliMiddlewares(store, [logger, logger1]);
// 测试代码
store.dispatch({
  type: \'INCREMENT\',
  payload: 2
})

更优雅的写法是定义oldDispatch为next.

redux中间件的实现

实际项目中,使用中间件的方式是:

import createLogger from \'redux-logger\';
import { createStore, applyMiddlewares} from \'redux\';

const initialState = {};

const configStore = () =>  {
  return createStore(reducers, initialState, applyMiddlewares(...middlewares));
}

实现createStore

// createStore(reducer, [preloadedState], [enhancer])
  const createStore = (reducer, preloadedState, enhancer) => { // 1
    if (enhancer !== undefined && typeof enhancer === \'function\') {
      return enhancer(createStore)(reducer, preloadedState); // 2
    }
    let store = preloadedState;
    let currentReducer = reducer;
    let listners = [];
    let isDispatching = false;
    if (isDispatching) {
      throw new Error(\'Reducers may not dispatch actions.\');
    }
    isDispatching = true;
    const getState = () => store;
    const dispatch = (action) => {
      store = currentReducer(store, action);
      return action;
    }
    // subscribe省略
    return { // 4
      getState,
      dispatch
    }
  }

  // (...args) => chain1(chain2(dispatch)(...args))
  const compose = (...funcs) => {
    if (funcs.length === 0) {
      return arg => arg;
    }
    if (funcs.length === 1) {
      return funcs[0];
    }
    return funcs.reduce((a, b) => (...args) => a(b)(...args)); // 6
  }

  // applyMiddlewares接收middlewares,返回一个函数接收createStore
  const applyMiddlewares = (...middlewares) => {
    // 这里的next相当于接收的createStore
    return (next) => {
      return (reducers, preloadedState) => {
        const store = next(reducers, preloadedState); // 3
        let dispatch = store.dispatch;
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (action) => dispatch(action)
        };
        const chain = middlewares.map(middleware => middleware(middlewareAPI));  // 5
        // chain: next => action => {}
        dispatch = compose(...chain)(store.dispatch);
        return {
          ...store,
          dispatch // 7
        }

      }
    }
  }

上图中的数字表示执行createStore时候的执行顺序。这几行代码包含有高阶函数、函数柯里化等概念。使用示例

// 示例
  const loggerMiddleware = store => next => action => {
    console.log(\'更新前\');
    console.log(store.getState());
    const returnValue = next(action);
    return returnValue;
  }

  const loggerMiddleware2 = store => next => action => {
    const returnValue = next(action);
    console.log(\'更新后\');
    console.log(store.getState());
    return returnValue;
  }
  // 创建
  const reducers = (state = { counter: 0 }, action) => {
    switch (action.type) {
      case \'INCREMENT\':
        return { ...state, counter: state.counter + action.payload };
      case \'DECREASE\':
        return { ...state, counter: state.counter - action.payload };
      default:
        return state;
    }
  }
  const store = createStore(reducers, {}, applyMiddlewares(...[loggerMiddleware, loggerMiddleware2]));
  // store.dispatch = loggerMiddleware({getState, dispatch})(loggerMiddleware2({getState, dispatch})(store.dispatch)({type: \'\'...}));
  store.dispatch({
    type: \'INCREMENT\',
    payload: 2
  })

react-redux connect

使用示例

connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
  • connect方法返回一个函数,该函数接口外部传入的业务组件,并且返回一个注入了store的React组件
  • 返回的React组件重新渲染外部传入的原始业务组件,并把connect中传入的mapStateToProps等于组件中原有的props合并

大致实现如下

const connect = (mapStateToProps, mapDispatchToProps, mergeProps, options = {}) => Component => {
    class WrappedComponent extends Component {
      constructor (props, context) {
        this.store = props.store || context.store;
        this.nextState = {}; // 把connect中传入的mapStateToProps等于组件中原有的props合并
      }
      render() {
        return <Component {...this.nextState} />
      }
    }
    // 返回WrappedComponent
    return WrappedComponent;
  }

本文来自网络整理,转载请注明原出处:https://segmentfault.com/a/1190000022001275

展开阅读全文

发表评论

登录后才能评论