redux & react-redux & react context

基于最近的两个项目对 redux、 react-redux 和 react context 作总结html

react-redux

redux 中有三大核心,分别是 storereduceraction。其中 store 全局惟一,用于保存 app 里的 state,reducer 控制 state 状态,action 用于描述如何改变 state,compose 用于多个函数参数调用,middleware 中间件,以下所示:react

import {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
  // __DO_NOT_USE__ActionTypes
} from "redux";
复制代码

建立 store

redux 中createStore函数用于建立 store,store 为一对象,其功能包括维持应用的 state,获取 state(getState),更新 state(dispatch),添加 listener(subscribe),第二个参数为初始值,第三个参数用于加强服务,多用于中间件,须要说明的是整个应用应该就只有一个 store,当须要拆分数据处理时候须要拆分 reducer 而不是建立多个 store。git

//@param{Func} reducer 为一个返回下一个state的函数
//@param{any} preloadedState 初始state
//@param{Func} enhancer 可用于第三方的
const store = createStore(reducer, preloadedState, enhancer);
//store 返回四个方法
//dispatch, 更新state
//subscribe,
//getState, 获取state
//replaceReducer
复制代码

建立 reducer

reducer 用于指定应用状态的变化如何响应 actions 并发送到 store,其为一个纯函数,接收旧的 state 和 action,返回新的 state,关于纯函数能够参考react pure functiongithub

const todoReducer = (state, action) => {
  return state;
};
复制代码

固然对于初始化的页面,咱们通常会给 state 个默认值或给空,redux

const initState = {
  page: 10
};
const initState = null;
const todoReducer = (state = initState, action) => {
  const type = action.type
  if(type === 'a') {
      return state + 'a';
  }
  if(type === 'b'){
      return {...state, {limit:2}}
  }
  return
};
复制代码

redux 还提供了个 combineReducers 方法,调用没个子 reducer,合并他们的结果到一个 state 中,用对象字面量来写以下,api

import { combineReducers } from "redux";
const todoApp = combineReducers({
  reducer1,
  reducer2
});
export default todoApp;
复制代码

注意:promise

  • 不要直接修改 state,使用 object.assign() 或...
  • 可能 action 未知,需传入 default

redux dispatch

在咱们建立 store 时候,store 会提供 dispath 函数,dispatch 会传入一个带 type 的 action,执行一个 listeners,并返回一个 action,dispatch 是惟一一个能够改变 state 的方式。固然 redux 还支持 dispatch 个 promise, observable 等,你须要使用第三方中间件包装你的 store。例如redux-thunkreact-router

function dispath(action) {
  //check is plain object
  //check action type
  //check is dispatching
  listener();
  //call listener
  return action;
}
dispatch({ type: ActionTypes.INIT });
复制代码

actions

既然咱们知道 action 是一个带 type 的对象,那么咱们能够把 action 抽象出来并发

export const UPDATE_ALERT_RES = "UPDATE_ALERT_RES";
export function todoAction(alertRes, ...rest) {
  return {
    type: UPDATE_ALERT_RES,
    payload: alertRes,
    ...rest
  };
}
复制代码

至此 redux 相关的就建立成功了,可是为了和 react 结合,咱们须要引入react-redux,react-redux 提供多个方法可供咱们使用app

react-redux

import {
  Provider,
  connectAdvanced,
  ReactReduxContext,
  connect,
  batch,
  useDispatch,
  useSelector,
  useStore,
  shallowEqual
} from "react-redux";
复制代码

其中provider能让组件层级中的 connet()方法都能得到到 redux store,咱们通常把这个置于根组件中,

import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import todoApp from "./reducers";
import App from "./components/App";
let store = createStore(todoApp);
render(
  <Provider store={store}> <App /> </Provider>,
  document.getElementById("root")
);
复制代码

另外一个咱们经常使用的函数connect可以链接 react 组件与 redux store,并返回一个与 store 链接的新组件

connect(
  [mapStateToProps],
  [mapDispatchToProps],
  [mergeProps],
  [options]
);
复制代码

根据以上这些,咱们就能够建立一个简单的基于 redux 的 app

index.js

import * as React from "react";
import { render } from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import Counter from "./counter";
const initStatus = {
  count: 0
};
function reducer(state = initStatus, action) {
  switch (action.type) {
    case "INCREMENT_COUNT":
      return { count: state.count + 5 };
    case "DECREMENT_COUNT":
      return { count: state.count - 1 };
    case "RESET_COUNT":
      return { count: 0 };
    default:
      return state;
  }
}
class App extends React.Component {
  render() {
    const store = createStore(reducer);
    return (
      <Provider store={store}> <Counter /> </Provider>
    );
  }
}
const rootElement = document.getElementById("root");
render(<App />, rootElement); 复制代码

counter.js

import * as React from "react";
import { connect } from "react-redux";
import { incrementCount } from "./action";
function mapStateToProps(state) {
  return {
    count: state.count
  };
}
class Counter extends React.Component {
  decreCount = () => {
    this.props.dispatch({ type: "DECREMENT_COUNT" });
  };
  increCount = () => {
    this.props.dispatch(incrementCount());
  };
  resetCount = () => {
    this.props.dispatch({ type: "RESET_COUNT" });
  };

  render() {
    return (
      <div className="App"> <h2>Count</h2> <div> <div> <button onClick={this.decreCount}>-</button> </div> <div> <span className="count">{this.props.count}</span> </div> <div> <button onClick={this.increCount}>+</button> </div> <div> <button onClick={this.resetCount}>重置</button> </div> </div> </div>
    );
  }
}
export default connect(mapStateToProps)(Counter);
复制代码

action.js

const INCREMENT_COUNT = "INCREMENT_COUNT";
export function incrementCount() {
  return { type: INCREMENT_COUNT };
}
复制代码

一个简单版的 react-redux 就介绍完毕,然而咱们的项目通常都会比较复杂,这样简单的并不适用,故此咱们作些改造

合并多个 reducer combineReducers

考虑到多个 reducer 不易操做,咱们把多个 reducer 合并成一个 reduer 来方便管理(其中APIReducer为与 API 操做有关的 reducer,咱们把与 API 相关的也抽象成一个 reducer,稍后介绍) rootReducer.js

import { routerReducer as routing } from "react-router-redux";
import { combineReducers } from "redux";
import reducer1 from "./reducer1";
import reducer2 from "./reducer2";
import reducer3 from "./reducer3";
import {
  reducer as formReducer,
  actionTypes as formActionTypes
} from "redux-form";
import { reducer as uiReducer } from "redux-ui";
import { reducers as APIReducer } from "~/API";
const rootReducer = combineReducers({
  APIReducer,
  reducer1,
  reducer2,
  reducer3,
  form: formReducer.plugin({
    HostForm: (state, action) => {
      if (!state || lodash.get(action, "XX") !== "XX") return state;
      //TODO SOMETHIN
      return state;
    }
  }),
  ui: uiReducer
});

export default rootReducer;
复制代码

合并多个 action

一样与 API 相关的也抽象成 Actions 方便管理,以防 action 错误,咱们使用个过滤器过滤未定义的 action rootActions.js

import lodash from "lodash";
import * as action1 from "./action1";
import * as action2 from "./action2";
import * as action3 from "./action3";
import { actions as APIActions } from "~/API";
const actions = Object.assign({}, APIActions, action1, action2, action3);
export function filterDispatchers(...args) {
  args.forEach(v => {
    if (!actions.hasOwnProperty(v)) {
      throw new Error(`filterDispatchers: No dispatcher named: ${v}`);
    }
  });
  return lodash.pick(actions, args);
}
export default actions;
复制代码

配置 store

import { createStore } from "redux";
import rootReducer from "../reducers";
import rootEnhancer from "./enhancer"; //处理token license等中间件
export default function configureStore(preloadedState) {
  const store = createStore(rootReducer, preloadedState, rootEnhancer);
  if (module.hot) {
    module.hot.accept("../reducers", () => {
      const nextRootReducer = require("../reducers").default;
      store.replaceReducer(nextRootReducer);
    });
  }
  return store;
}
复制代码

配置 selectors

reselect能够用于建立可记忆的、可组合的 selector 函数,高效计算衍生数据

componets.js

export const selector1 = state => state.selector1;
export const selector2 = state => state.selector2;
export const selector3 = state => state.selector3;
复制代码

selectors.js

import lodash from "lodash";
import { createSelector } from "reselect";
import * as componentSelectors from "./components";
import { selectors as APISelectors } from "~/API";
const selectors = Object.assign(componentSelectors, APISelectors);
export function filterSelectors(...args) {
  return function mapStateToProps(state) {
    const inputSelectors = args.map(v => {
      const selector = `${v}Selector`;
      if (!selectors.hasOwnProperty(selector)) {
        throw new Error(`filterSelectors: No selector named: ${selector}`);
      }
      return selectors[selector];
    });
    return createSelector(
      inputSelectors,
      (...selected) => lodash.zipObject(args, selected)
    )(state);
  };
}

export default selectors;
复制代码

常见页面结构以下,connect 多个 state、action, 再用 compose 组合,其余 HOC,socket,page 等 page.js

import { compose } from "redux";
import { connect } from "react-redux";
import { filterSelectors } from "~/selectors";
import { filterDispatchers } from "~/actions";
const connector = compose(
  connect(
    filterSelectors("state"),
    filterDispatchers("action")
  ),
  Hoc()
);
class Page extends React.Componet {}
export default connector(Page);
复制代码

API redux

异步 redux 相似于同步,只需添加中间件处理 fetch 数据,reducer 为 fetching(state, action),action 状态为 RESTFul API 加上返回状态 例如

const resultType = ['REQUEST','SUCCESS', 'FAILURE'];
  const methodd = ['PATCH','GET','POST','PUT']
  const actionType = PATCH_SOMEAPI_SUCCESS;

复制代码

callAPI 可参考官方示例redux-promisereddid API

react context

context是由 react 原生的跨组件数据传输方案,其 API 包括 React.creatContext, Context.Provider, Context.Consumer,

creatContext

建立 context,能够指定默认值,也可在初始页面 fetch,咱们选择基本的 createContext 建立方式,指定一个共享的对象 something 和一个更新改对象的方法,updateSomething()

context.js

import React from "react";
export const GlobalContext = React.createContext({
  user: {},
  updateUser() {},
  something: {},
  updateSomething: {}
});
复制代码

在要共享的页面提供该 context,把要共享的值传出去, provider props,通常用于 dashsboard 页面,让子组件都能共享 provider.js

import {GlobalContext} from '/context'
    class page extends React.componet{
        updateUser() {
            return update
        }
        render() {
            const response  = this.fetch();
            return (
            <GlobalContext.Provider
                value={
                    user:this.response.user,
                    updateUser: this.updateUser
                }
            >
            <GlobalContext.Provider>
            )
        }
    }

复制代码

子组件做为消费者拿到 context consumer.js

import { GlobalContext } from "/context";
class page extends React.componet {
  render() {
    <div>111</div>;
  }
}
export default props => (
  <GlobalContext.Consumer>
    {({ user, updateUser }) => (
      <Resources {...props} user={user} updateUser={updateUser} />
    )}
  </GlobalContext.Consumer>
);
复制代码
相关文章
相关标签/搜索