yanyue404/blog

Redux入门

yanyue404 opened this issue · 0 comments

介绍

Redux 是 JavaScript 状态容器, 试图让 state 的变化变得可预测。

使用场景

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了 WebSocket
  • View 要从多个来源获取数据

flux 架构

flux 的单向数据流图

/*
                 _________               ____________               ___________
                |         |             |            |             |           |
                | Action  |------------▶| Dispatcher |------------▶| callbacks |
                |_________|             |____________|             |___________|
                     ▲                                                   |
                     |                                                   |
                     |                                                   |
 _________       ____|_____                                          ____▼____
|         |◀----|  Action  |                                        |         |
| Web API |     | Creators |                                        |  Store  |
|_________|----▶|__________|                                        |_________|
                     ▲                                                   |
                     |                                                   |
                 ____|________           ____________                ____▼____
                |   User       |         |   React   |              | Change  |
                | interactions |◀--------|   Views   |◀-------------| events  |
                |______________|         |___________|              |_________|
*/

结合 Redux 分析流程:

ActionCreator -> Action -> dispatcher -> middleware  -> reducers  -> Store   -> Change events  -> React Views

核心概念

action: 描述行为的指示器,是一个用于描述已发生事件的普通对象。

state: 应用的全局状态,唯一改变 state 的方法就是触发 action。

reducer:state 函数更新规则,接收 state 和 action,并返回新的 state

Demo

actions

function increment(num) {
  return {
    type: 'INCREMENT',
    num
  };
}

function decrement(num) {
  return {
    type: 'DECREMENT',
    num
  };
}

reducers

var initCount = {
  count: 0
};
function counter(state, action) {
  if (!state) {
    return initCount;
  }
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + Number(action.num) };
    case 'DECREMENT':
      return { count: state.count - Number(action.num) };
    default:
      return state;
  }
}

不要修改 state,为 action 返回一个新的 state;
在遇到未知 action 时,默认情况下一定要返回旧的 state

store

store 是一个对象,使用 Redux 提供的createStore方法来生成,我们需要将 Reducer 作为参数传进去,在本例中:

var store = Redux.createStore(counter);

store 拥有以下方法

  • 通过store.getState()f 方法 获取 state;
  • 通过store.dispatch(action)方法来更新 state;
  • 通过store.subscribe(listener)方法来注册监听器,state 变化时自动执行该函数;

通过 store.getState()获取 state,并根据 state 值来设置 span 的初始值

function renderValue() {
  document.querySelector('span').innerHTML = store.getState().count;
}

// 首次执行
renderValue();

注册监听器,每当 state 发生变化时执行上面的渲染函数

store.subscribe(renderValue);

整合逻辑,触发 action

最后通过 store.dispatch(action)来触发修改 state 的操作,写在事件处理程序中,点击按钮时修改 state:

document.querySelector('.increment').onclick = function() {
  let num = document.querySelector('input').value;
  setTimeout(() => {
    store.dispatch(increment(num));
  }, 0);
};
document.querySelector('.decrement').onclick = function() {
  let num = document.querySelector('input').value;
  store.dispatch(decrement(num));
};

middleware

中间件,redux 中createStore的第三个参数支持添加中间件。

import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';

const error = store => next => action => {
  try {
    next(action);
  } catch (e) {
    console.log('error', e);
  }
};
const store = createStore(rootReducer, {}, applyMiddleware(logger, error));

上图中的logger中间件可以自己书写:

const logger = store => next => action => {
  console.log('dispatching', action);
  let result = next(action);
  console.log('next state', store.getState());
  return result;
};

const logger = function(store) {
  return function(next) {
    return function(action) {
      console.log('dispatching...', action);
      let result = next(action);
      console.log('next State', store.getState());
      return result;
    };
  };
};

react-redux

react-redux 的作用是将 redux 的的反应更好的流动(绑定到)reac 应用上。

作用在于将由 redux 创建的 store 传递到内部组件中,内部组件可以使用这个 store 并提供对 state 的更新。

所以说Provider可以只在 react 应用最外包裹层注入 store 就可以了。

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <Route path="/" component={App}>
        <Route path="foo" component={Foo}/>
        <Route path="bar" component={Bar}/>
      </Route>
    </Router>
  </Provider>,
  document.getElementById('root')

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

connect()一共有四个参数,常用的有两个mapStateToPropsmapDispatchToProps

  • mapStateToProps:把状态绑定到组件的属性当中。我们定义的 state 对象有哪些属性,在我们组件的 props 都可以查阅和获取。
const mapStateToProps = state => {
  return {
    counter: state.counter
  };
};

state 相当于 store.getState() 通过 connect 绑定到组件的 props 上进行获取,返回的对象键可以自定义;

  • mapDispatchToProps:在 redux 中介绍过,用 store.dispatch(action)来发出操作,那么我们同样可以把这个方法封装起来,即绑定到我们的方法中。
import { increment, decrement } from './actions'; // 少量action

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch(increment()),
    decrement: () => dispatch(decrement())
  };
};

补充

单个声明式导出 action 方法和多个一块导出,这种 props 数据比较清晰

import { increment, decrement } from './actions'; // 少量action
// import * as types from './actions'; // 多个action

另一种方法借用bindActionCreatorsapi,这样会将所有的 action 一同绑定,适情况使用。

import * as types from './actions'; // 多个action

const mapDispatchToProps = dispatch => {
  return bindActionCreators(types, dispatch);
};

最后 connect()将 state 和 dispatch 都绑定到导出的组件上,redux 数据就经过 react-redux 流动到了 react 组件上。

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

Redux DevTools

参考