Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redux入门 #34

Open
yanyue404 opened this issue Jun 7, 2018 · 0 comments
Open

Redux入门 #34

yanyue404 opened this issue Jun 7, 2018 · 0 comments

Comments

@yanyue404
Copy link
Owner

yanyue404 commented Jun 7, 2018

介绍

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

参考

@yanyue404 yanyue404 added the react label Jun 7, 2018
@yanyue404 yanyue404 removed the react label Sep 2, 2019
@yanyue404 yanyue404 changed the title redux入门 Redux入门 Oct 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant