初识redux

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

一张图描述redux的过程


image.png

我们可以大致理解为通过UI去触发action,action发送给了纯函数reducer,通过reducer去update全局对象store,当改变了store里的state时会重新渲染页面。

最基本的列子
首先

yarn add redux

src/index.js

import { createStore } from 'redux'; // 从redux包中导入createStore方法

const store = createStore(reducer); //使用reducer纯函数作为第一个参数创建store

// 纯函数方法 请不要在reducer中修改传入参数,执行有副作用的操作,调用非纯函数
// 在 reducer中描述了action如何把state转换成下一个state
function reducer(state, action) {
  if(action.type === 'changeState') {
    return action.payload.newState;
  }
  return 'State';
}

console.log(store.getState()) // 使用store.getState()获取当前state

// action 普通对象
const action = {
  type: 'changeState',
  payload: {
    newState: 'NewState'
  }
}

store.dispatch(action); // dispatch 将action发给store
console.log(store.getState()) // 再次获取看是否发生变化

传送门 https://github.com/z1811021/redux-study/tree/master/sample1

使用combineReducers

接收一个拆分后 reducer 函数组成的对象,返回一个新的 Reducer 函数,然后就可以调用这个函数了

import { createStore, combineReducers } from 'redux'; // 引入combineReducers

function reducer1(state=[], action) {   // 设置初始state
  return state;
}

function reducer2(state=[], action) {
  switch (action.type) {
    case 'updateReducer1':
      return action.payload.reducer2;
    default:
      return state;
  }
}

const allReducers = combineReducers({  //使用combineReducers 将两个reducer变为一个
  reducer1,
  reducer2
})

const updateReducer1action = {
  type: 'updateReducer1',
  payload: {
    reducer2: 11
  }
}
const store = createStore(
  allReducers,
  {reducer1:[1], reducer2:[2]}, // 替换为allReducers 并且设置初始state 作为第二个参数
  window.devToolsExtension ? window.devToolsExtension() : f => f
);  
console.log(store.getState())
store.dispatch(updateReducer1action);
console.log(store.getState())

传送门https://github.com/z1811021/redux-study/tree/master/sample2

使用react-dedux

版本为6.x 要求react版本later than 16.4

在react-dedux中我们需要使用到Provider

它是通过context将store传给子组件

使用connect
用connect()将state和action连接起来

connect一共四个参数connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

此时我们的数据结构先将会变成,大家可以先去github clone下来

https://github.com/z1811021/redux-study/tree/master/sample3

├── config-overrides.js
├── package.json
├── public
|  ├── favicon.ico
|  ├── index.html
|  └── manifest.json
├── README.md
├── src
|  ├── app
|  ├── asset
|  |  └── logo.svg
|  ├── components
|  ├── index.js
|  ├── index.scss
|  ├── layouts
|  ├── serviceWorker.js
|  └── views
|     └── login
|        ├── app.test.js
|        ├── index.js
|        ├── index.scss
|        ├── passwordActions.js
|        ├── passwordReducer.js
|        ├── usernameActions.js
|        └── usernameReducer.js
├── yarn-error.log
└── yarn.lock

Provider

在所有组件的顶层使用Provider组件给整个程序提供store

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './views/login';
import * as serviceWorker from './serviceWorker';
import { createStore, combineReducers } from 'redux'; // 引入combineReducers
import { Provider } from 'react-redux';
import reducer1 from './views/login/usernameReducer.js'
import reducer2 from './views/login/passwordReducer.js'


const allReducers = combineReducers({  //使用combineReducers 将两个reducer变为一个
  reducer1,
  reducer2
})

const store = createStore(
  allReducers,
  {reducer1:[1], reducer2:[2]}, // 替换为allReducers 并且设置初始state 作为第二个参数
  window.devToolsExtension ? window.devToolsExtension() : f => f
);  

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  , document.getElementById('root')); // Provider使用context将store传给子组件

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.register();

接下来我们将所有的action和reducer单独放在不同的file里

在action里我们使用const让变量仅用于action
src/view/login/passwordActions.js

export const UPDATE_PASSWORD = 'updatePassword'  //使用const让UPDATE_PASSWORD仅用于action

export function updateReducer2action (password) {
  return {
    type: UPDATE_PASSWORD,
    payload: {
      reducer2: password
    }
  }
}

connect

在src/view/login/index.js中我们使用connect将state和action创建的函数绑定到组件

一共有三个参数,一般我们只使用两个 mapStateToProps,mapDispatchToProps

mapStateToProps

mapStateToProps用来选择从store过来的data

当store state发生改变的时候这个方法就会被call
这个方法接受整个store的state,我们可以返回对象来获取我们想要的

mapDispatchToProps

我们可以define mapDispatchToProps 为 function 和 对象

我们建议define为对象 除非我们需要自定义dispatching behavior。

所以我们使用object举例

将所有的action创建的函数传到组件同名属性,无须使用dispatch直接使用prsps调用

src/view/login/index.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import  { updateReducer2action } from './passwordActions.js';
import  { updateReducer1action } from './usernameActions.js';
import { Form, Icon, Input, Button, message } from 'antd';
import './index.scss';
const FormItem = Form.Item;


class AppLogin extends Component {
  constructor(props) {
    super(props);
    this.onUpdatePassword = this.onUpdatePassword.bind(this)
    this.onUpdateUsername = this.onUpdateUsername.bind(this)
  }
  handleSubmit = (e) => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);
        if (values.userName === 'admin' && values.password === 'test'){
          message.success('login successful');
        } else {
          message.error('login unsuccessful');
        }
      }
    });
  }
  onUpdatePassword(e){
    this.props.onUpdatePassword(e.target.value);
  }
  onUpdateUsername(e){
    this.props.onUpdateUsername(e.target.value);
  }
  componentDidUpdate(){
    console.log(this.props)
  }
  render() {
    const { getFieldDecorator } = this.props.form;
    return (
      <div className="App">
        <div className="container">
          <Form onSubmit={this.handleSubmit} className="login-form">
            <FormItem>
              {getFieldDecorator('userName', {
                rules: [{ required: true, message: 'Please input your username!' }],
              })(
                <Input  onChange={this.onUpdateUsername} prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
              )}
            </FormItem>
            <FormItem>
              {getFieldDecorator('password', {
                rules: [{ required: true, message: 'Please input your Password!' }],
              })(
                <Input onChange={this.onUpdatePassword} prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
              )}
            </FormItem>
            <FormItem>
              <Button type="primary" htmlType="submit" className="login-form-button">
                Log in
              </Button>
            </FormItem>
          </Form>
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => ({  //ES6中箭头函数返回对象 第一个参数将state传递给props  
  username:state.reducer1,
  password:state.reducer2
})

const mapDispatchToProps = {
  onUpdatePassword: updateReducer2action,
  onUpdateUsername: updateReducer1action
}   //第二个参数将所有的action创建的函数传到组件同名属性,无须使用dispatch直接使用prsps调用

const App = Form.create()(AppLogin);
export default connect(mapStateToProps,mapDispatchToProps)(App);  


redux thunk

接下来我们将使用 redux thunk
action函数只能返回action对象,但是用中间件加工后就可以返回更多,所以redux thunk就是来做这个的。 通过返回的函数延迟dispatch或者在指定条件下才dispatch。这个函数接受store的两个方法dispatch和getState。

我们将使用redux-thunk返回一个函数,在之中使用axios调取某api

我们将使用到compose和applyMiddleware
compose 其作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。compose(f, g, h) is identical to doing
(...args) => f(g(h(...args))).

applyMiddleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './views/login';
import * as serviceWorker from './serviceWorker';
import { applyMiddleware, compose, createStore, combineReducers } from 'redux'; // 引入combineReducers
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import reducer1 from './views/login/usernameReducer.js'
import reducer2 from './views/login/passwordReducer.js'


const allReducers = combineReducers({  //使用combineReducers 将两个reducer变为一个
  reducer1,
  reducer2
})

const allStoreEnhancers = compose(
  applyMiddleware(thunk), // 使用后action可以返回函数或其他形式不局限于对象
  window.devToolsExtension ? window.devToolsExtension() : f => f
) // 其作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。

const store = createStore(
  allReducers,
  {reducer1:[1], reducer2:[2]}, // 替换为allReducers 并且设置初始state 作为第二个参数
  allStoreEnhancers
);  


ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  , document.getElementById('root')); // Provider使用context将store传给子组件

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.register();

接下来修改任意一个action
src/view/login/usernameActions.js

import axios from 'axios';
export const UPDATE_USERNAME = 'updateUsername';

export function updateReducer1action(username) {
  return {
    type: UPDATE_USERNAME,
    payload:{
      reducer1: username
    }
  }
}

// 添加一个方法返回一个函数
export function getRequest(username) {
  return dispatch => {
    axios({
      method: 'get',
      url: 'https://randomuser.me/api',
    })
    .then((res)=>{
      console.log(res)
    })
    .catch((err)=>{
      console.log(err)
    })
  }
}

接下来我们在index中打印

import React, { Component } from 'react';
import { connect } from 'react-redux';
import  { updateReducer2action } from './passwordActions.js';
import  { updateReducer1action, getRequest } from './usernameActions.js';
import { Form, Icon, Input, Button, message } from 'antd';
import './index.scss';
const FormItem = Form.Item;


class AppLogin extends Component {
  constructor(props) {
    super(props);
    this.onUpdatePassword = this.onUpdatePassword.bind(this)
    this.onUpdateUsername = this.onUpdateUsername.bind(this)
  }
  handleSubmit = (e) => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);
        if (values.userName === 'admin' && values.password === 'test'){
          message.success('login successful');
        } else {
          message.error('login unsuccessful');
        }
      }
    });
  }
  onUpdatePassword(e){
    this.props.onUpdatePassword(e.target.value);
  }
  onUpdateUsername(e){
    this.props.onUpdateUsername(e.target.value);
  }
  componentDidUpdate(){
    this.props.onGetRequest();
    console.log(this.props)
  }
  render() {
    const { getFieldDecorator } = this.props.form;
    return (
      <div className="App">
        <div className="container">
          <Form onSubmit={this.handleSubmit} className="login-form">
            <FormItem>
              {getFieldDecorator('userName', {
                rules: [{ required: true, message: 'Please input your username!' }],
              })(
                <Input  onChange={this.onUpdateUsername} prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
              )}
            </FormItem>
            <FormItem>
              {getFieldDecorator('password', {
                rules: [{ required: true, message: 'Please input your Password!' }],
              })(
                <Input onChange={this.onUpdatePassword} prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
              )}
            </FormItem>
            <FormItem>
              <Button type="primary" htmlType="submit" className="login-form-button">
                Log in
              </Button>
            </FormItem>
          </Form>
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => ({  //ES6中箭头函数返回对象 第一个参数将state传递给props  
  username:state.reducer1,
  password:state.reducer2
})

const mapDispatchToProps = {
  onUpdatePassword: updateReducer2action,
  onUpdateUsername: updateReducer1action,
  onGetRequest: getRequest
}   //第二个参数将所有的action创建的函数传到组件同名属性,无须使用dispatch直接使用prsps调用

const App = Form.create()(AppLogin);
export default connect(mapStateToProps,mapDispatchToProps)(App);  


传送门 https://github.com/z1811021/redux-study/tree/master/sample4

connected-react-router

同步router state 和 redux store 通过 uni-directional flow (i.e. history -> store -> router -> components).

首先我们要用到history的createBrowserHistory

然后我们要create root reducer作为一个函数将history作为参数并且返回给reducer

添加路由reducer通过传递history给connectRouter

使用routerMiddleware(history)如果需要dispatch history给connectRouter

import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './views/login';
import * as serviceWorker from './serviceWorker';
import { applyMiddleware, compose, createStore, combineReducers } from 'redux'; // 引入combineReducers
import { Provider } from 'react-redux';
import { connectRouter } from 'connected-react-router';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router'
import { routerMiddleware } from 'connected-react-router';
import { createBrowserHistory } from 'history';
import thunk from 'redux-thunk';
import reducer1 from './views/login/usernameReducer.js'
import reducer2 from './views/login/passwordReducer.js'


const allReducers = (history) => combineReducers({  //使用combineReducers 将两个reducer变为一个
  router: connectRouter(history), // 添加路由reducer通过传递history给connectRouter
  reducer1,
  reducer2
})

const history = createBrowserHistory();

const allStoreEnhancers = compose(
  applyMiddleware(routerMiddleware(history),thunk), //使用routerMiddleware(history)如果需要dispatch history给connectRouter
  window.devToolsExtension ? window.devToolsExtension() : f => f
) // 其作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。

const store = createStore(
  allReducers(history), //将history传递
  {reducer1:[1], reducer2:[2]}, // 替换为allReducers 并且设置初始state 作为第二个参数
  allStoreEnhancers
);  


ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Switch>
        <Route exact path="/" render={() => (<App />)} />
      </Switch>
    </ConnectedRouter>
  </Provider>
  , document.getElementById('root')); // Provider使用context将store传给子组件

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.register();

接下来我们就可以在mapStateToProps中使用state.router使用

const mapStateToProps = state => ({  //ES6中箭头函数返回对象 第一个参数将state传递给props  
  username:state.reducer1,
  password:state.reducer2,
  router: state.router // 获取到router
})

这里不做过多解释,接下来我会专门写一篇关于react-router 4的文章。

connected-react-router https://github.com/supasate/connected-react-router

传送门 (https://github.com/z1811021/redux-study/tree/master/sample4)

完成版

接下来我们将重新重构整个目录结构 不把所有的初始action和reducer放在index中

把初始的action和reducer放在/app

目录机构如下

├── config-overrides.js
├── package.json
├── public
|  ├── favicon.ico
|  ├── index.html
|  └── manifest.json
├── README.md
├── src
|  ├── app
|  |  ├── action.js
|  |  ├── createStore.js
|  |  └── reducer.js
|  ├── asset
|  |  └── logo.svg
|  ├── components
|  ├── index.js
|  ├── index.scss
|  ├── layouts
|  ├── serviceWorker.js
|  └── views
|     └── login
├── yarn-error.log
└── yarn.lock

传送门 https://github.com/z1811021/redux-study/tree/master/sample6

接下来我们就告一段落了,如果需要具体介绍我们可以接着往下看

state

当使用普通对象来描述应用的 state 时。例如,todo 应用的 state 可能长这样:

{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}

action

要想更新 state 中的数据,你需要发起一个 action。Action 就是一个普通 JavaScript 对象它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。

/*
 * action 类型
 */

export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'

/*
 * 其它的常量
 */

export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE'
}

Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。

在 Redux 中的 action 创建函数只是简单的返回一个 action:

/*
 * action 创建函数
 */

export function addTodo(text) {
  return { type: ADD_TODO, text }
}

export function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

export function setVisibilityFilter(filter) {
  return { type: SET_VISIBILITY_FILTER, filter }
}

reducer

强制使用 action 来描述所有变化带来的好处是可以清晰地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。action 就像是描述发生了什么的指示器。最终,为了把 action 和 state 串起来,开发一些函数,这就是 reducer。再次地,没有任何魔法,reducer 只是一个接收 state 和 action,并返回新的 state 的函数。 对于大的应用来说,不大可能仅仅只写一个这样的函数,所以我们编写很多小函数来分别管理 state 的一部分:

import { combineReducers } from 'redux'
import {
  ADD_TODO,
  TOGGLE_TODO,
  SET_VISIBILITY_FILTER,
  VisibilityFilters
} from './actions'

const { SHOW_ALL } = VisibilityFilters

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

再开发一个 reducer 调用这两个 reducer,进而来管理整个应用的 state:

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

Store 就是把它们联系到一起的对象。

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。

根据已有的 reducer 来创建 store 是非常容易的。在前一个章节中,我们使用 combineReducers() 将多个 reducer 合并成为一个。现在我们将其导入,并传递 createStore()。

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

现在我们已经创建好了 store ,让我们来验证一下!虽然还没有界面,我们已经可以测试数据处理逻辑了。

import {
  addTodo,
  toggleTodo,
  setVisibilityFilter,
  VisibilityFilters
} from './actions'

// 打印初始状态
console.log(store.getState())

// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(() =>
  console.log(store.getState())
)

// 发起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))

// 停止监听 state 更新
unsubscribe();

redux thunk

action函数只能返回action对象,但是用中间件加工后就可以返回更多,所以redux thunk就是来做这个的。
通过返回的函数延迟dispatch或者在指定条件下才dispatch。这个函数接受store的两个方法dispatch和getState。

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

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

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

function incrementIfOdd() {
  return (dispatch, getState) => {
    const value = getState();
    if (value % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

function incrementAsync(delay = 5000) {
  return dispatch => { 
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(counter, applyMiddleware(thunk));

let currentValue = store.getState();
store.subscribe(() => {
  const previousValue = currentValue;
  currentValue = store.getState();
  console.log('pre state:', previousValue, 'next state:', currentValue);
}
);

store.dispatch(increment());

store.dispatch(incrementIfOdd());

store.dispatch(incrementAsync());

store.dispatch(decrement());

React与redux连接

在所有组件顶层用Provider组件提供store

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import counter from './reducers';
import Connect1 from './containers/Connect1';

const store = createStore(counter, applyMiddleware(thunk));
const rootEl = document.getElementById('root');

ReactDOM.render(
  <Provider store={store}>
    <div>
      <h2>使用react-redux连接</h2>
      <ul>
        <li>
          connect()的前两个参数分别为函数和对象:
          <Connect1 />
        </li>
      </ul>
    </div>
  </Provider>, rootEl);

使用connect()将state和action连接起来

import Counter from '../components/Counter';
import { connect } from 'react-redux';
import * as ActionCreators from '../actions';

export default connect(
  state => ({ counter: state.counter }),
  ActionCreators
)(Counter);

connect第一个参数是将state.counter传递给counter

第二个参数是将action创建函数传递到组件同名属性比如(increment被传递到props.increment),同时每个action创建函数隐式绑定了dispatch方法,因此可以直接通过props调用action方法。无须使用dispatch方法。

import React, { PropTypes } from 'react';

function Counter({ counter, increment, decrement, incrementIfOdd, incrementAsync }) {
  return (
    <p>
      Clicked: {counter} times
      {' '}
      <button onClick={increment}>+</button>
      {' '}
      <button onClick={decrement}>-</button>
      {' '}
      <button onClick={incrementIfOdd}>Increment if odd</button>
      {' '}
      <button onClick={() => incrementAsync()}>Increment async</button>
    </p>
  );
}

Counter.propTypes = {
  counter: PropTypes.number.isRequired,
  increment: PropTypes.func.isRequired,
  incrementIfOdd: PropTypes.func.isRequired,
  incrementAsync: PropTypes.func.isRequired,
  decrement: PropTypes.func.isRequired
};

export default Counter;

reducer counter.js

import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions';

export default function counter(state = 0, action) {
  switch (action.type) {
    case INCREMENT_COUNTER:
      return state + 1;
    case DECREMENT_COUNTER:
      return state - 1;
    default:
      return state;
  }
}

index.js

import { combineReducers } from 'redux';
import counter from './counter';

/**
 * 虽然本例中只有一个reducer,但还是使用了`combineReducers`来进行合并,便于后期的拓展。
 * 在进行合并后,计数器的数值将被转移到`state.counter`中。
 */

const rootReducer = combineReducers({
  counter,
});

export default rootReducer;

action index.js

// 这里将action对象的type属性值写成了常量,便于reducer引用,减少了出错的概率。
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

export function increment() {
  return {
    type: INCREMENT_COUNTER,
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER,
  };
}

export function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

export function incrementAsync(delay = 1000) {
  return dispatch => {
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}

Provider组件负责给程序提供store,而connect()则负责生成新的名为Connect的组件,Connect组件在context拿到store后,从store获取state和dispatch,最后将state和经过dispatch加工后的action创建函数连接到组件上。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容