美文网首页
初识redux

初识redux

作者: 龚达耶 | 来源:发表于2018-09-06 23:19 被阅读0次

    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创建函数连接到组件上。

    相关文章

      网友评论

          本文标题:初识redux

          本文链接:https://www.haomeiwen.com/subject/wxefgftx.html