23. redux

作者: dwy_interesting | 来源:发表于2018-11-13 21:04 被阅读0次

    redux数据请求机制


    image.png

    图片来源于:https://segmentfault.com/img/bVRQRK?w=1205&h=618

    action & actionCreator
    action creator 就是函数而已,负责构建一个 action (是的,action creator 这个名字已经很明显了)并返回它。通过几行简单的代码就可以解释清楚了!

    const actionCreator = function () {
      return {
        type : 'AN_ACTION'
      }
    }
    
    

    一般约定 action 是一个拥有 type 属性的对象。

    console.log(actionCreator())
    //  { type: 'AN_ACTION' }
    

    reducer
    Reducer 函数只是一个纯函数,它接收应用程序的当前状态以及发生的 action,然后返回修改后的新状态(或者有人称之为归并后的状态)。Reducer 函数是 action 的订阅者。

    const reducer = function (state = {}, action) {
      console.log('reducer was called with state', state, 'and action', action);
    
      return state;
    }
    

    Store
    以上,action描述“发生了什么”,而reducer根据action来更新state。但是他们两者之间是如何关联的呢?

    不用担心,Redux 会帮你把action和reducer连接起来。

    我们把 Redux实例称为 store 并用以下方式创建:

    import { createStore } from 'redux'
    
    const store_0 = createStore(() => {})
    

    注意:在createStore时,需要给它传入一个 reducer 函数。

    每当一个action发生时,Redux都能调用这个函数。往 createStore 传 Reducer 的过程就是给 Redux绑定 action处理函数(也就是Reducer)的过程。

    接下来,试着在 Reducer 中打印一些 log

    const reducer = function (...args) {
      console.log('Reducer was called with args', args)
    }
    
    const store_1 = createStore(reducer)
    // 输出:Reducer was called with args [ undefined, { type: '@@redux/INIT' } ]
    

    我们没有dispatch(分发)任何action,但是reducer被调用了!这是由于初始化应用state的时候,Redux dispatch 了一个初始化的 action ({ type: '@@redux/INIT' })。reducer的入参为(state, action)。state还没有被初始化,自然为undefined。

    如何读取store中的state?

    Redux为我们提供了store.getState()方法。

    import { createStore } from 'redux'
    
    const reducer_2 = function (state = {}, action) {
      console.log('reducer_2 was called with state', state, 'and action', action)
    
      return state;
    }
    
    const store_2 = createStore(reducer_2)
    // 输出: reducer_2 was called with state {} and action { type: '@@redux/INIT' }
    
    console.log('store_2 state after initialization:', store_2.getState())
    // 输出: store_2 state after initialization: {}
    

    如何dispatch action?

    我们需要使用store.dispatch(action)方法。

    // 接以上代码
    const anAction = {
      type : 'AN_ACTION'
    }
    store_2.dispatch(anAction);
    // 输出:reducer_2 was called with state {} and action { type: 'AN_ACTION' }
    combineReducers
    combineReducer用于合并Reducers,并且合并对应的State。
    
    const userReducer  = function (state = {}, action) {
      console.log('userReducer was called with state', state, 'and action', action)
    
      switch (action.type) {
        // etc.
        default:
          return state;
      }
    }
    const itemsReducer = function (state = [], action) {
      console.log('itemsReducer was called with state', state, 'and action', action)
    
      switch (action.type) {
        // etc.
        default:
          return state;
      }
    }
    import { createStore, combineReducers } from 'redux'
    
    const reducer = combineReducers({
      user  : userReducer,
      items : itemsReducer
    })
    
    // 输出:
    // userReducer was called with state {} and action { type: '@@redux/INIT' }
    // userReducer was called with state {} and action { type: '@@redux/PROBE_UNKNOWN_ACTION_9.r.k.r.i.c.n.m.i' }
    // itemsReducer was called with state [] and action { type: '@@redux/INIT' }
    // itemsReducer was called with state [] and action { type: '@@redux/PROBE_UNKNOWN_ACTION_4.f.i.z.l.3.7.s.y.v.i' }
    
    var store_0 = createStore(reducer)
    
    // 输出:
    // userReducer was called with state {} and action { type: '@@redux/INIT' }
    // itemsReducer was called with state [] and action { type: '@@redux/INIT' }
    
    console.log('store_0 state after initialization:', store_0.getState())
    // 输出:
    // store_0 state after initialization: { user: {}, items: [] }
    

    回过头来看看文章开头的数据流向图
    View组件通过click等事件,dispatch一个(actionCreator返回的)action,通过Store把当前状态state和action传递给订阅者reducer函数,reducer返回一个新的状态存储在Store中,Store又把新的State传递给View组件触发组件更新。

    为了将Redux和React联系到一起。就需要用到React-Redux这个库。

    import { connect } from 'react-redux'
    const containerComponent = connect(mapStateToProps, mapDispatchToProps)(presentationalComponent)
    简单来说,mapStateToProps和mapDispatchToProps就是分别把Redux的state,和dispatch(action)映射到React组件中作为props。connect将展示组件(presentationalComponent)封装成高阶的容器组件(containerComponent)。state的更新意味着props更新。
    
    

    上诉摘自于https://segmentfault.com/a/1190000010407887

    请求机制:

    image.png

    接下来上项目吧:

    1. 安装依赖:
      cnpm install redux react-redux redux-thunk axios --save
    2. 目录结构如下


      image.png

    3.代码如下:
    (1)src/index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'react-redux';
    import App from './App';
    
    import store from './store';
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>, 
      document.getElementById('root')
    );
    

    (2) src/App.js

    import React, { Component } from 'react';
    
    import { connect } from 'react-redux';
    
    import { requestBlog } from './actions/blog';
    
    class App extends Component {
      //第一步:通过UI动作进入后,在componentDidMount请求数据
      //通过requestBlog()找到Blog.js的actionCreater
      componentDidMount() {
        this.props.requestBlog();
      }
      render() {
        //随着blog.js中的值被更改,this.props的值(依赖于state.blog的blogList)也被更改
        const {
          blogList,
          isLoading
        } = this.props;
        return (
          <div className="App">
            {
              isLoading
              ?
              <div className="loading">加载中……</div>
              :
              <ul>
                {
                  blogList.map(blog => {
                    const {
                      id,
                      title,
                      body
                    } = blog;
                    
                    return (
                      <li key={id}>
                        <h2>{title}</h2>
                        <p>{body}</p>
                      </li>
                    )
                  })
                }
              </ul>
            }
          </div>
        );
      }
    }
    
    const mapState = state => {
      return {
        blogList: state.blog.list,
        isLoading: state.ux.isLoading
      }
    }
    
    export default connect(mapState, { requestBlog })(App);
    
    

    (3)src/store.js

    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    
    import rootReducer from './reducers';
    
    export default createStore(
      rootReducer,
      applyMiddleware(thunk)
    );
    

    (4) src/actions/actionType.js

    export const SHOW_LOADING = 'SHOW_LOADING';
    export const HIDE_LOADING = 'HIDE_LOADING';
    export const REQUEST_BLOG_SUCCESS = 'REQUEST_BLOG_SUCCESS';
    

    (5) src/actions/ux.js

    import {
      HIDE_LOADING,
      SHOW_LOADING
    } from './actionType';
    
    export const showLoading = () => {
      return {
        type: SHOW_LOADING
      }
    }
    
    export const hideLoading = () => {
      return {
        type: HIDE_LOADING
      }
    }
    

    (6) src/actions/blog.js

    import axios from 'axios';
    
    import {
      REQUEST_BLOG_SUCCESS
    } from './actionType';
    
    import {
      showLoading,
      hideLoading
    } from './ux';
    
    //App.js中的componentDidMount中调用的requestBlog
    export const requestBlog = () => {
      return (dispatch) => {
        // 开始请求之前先显示loading
        dispatch(showLoading());
        axios.get('http://jsonplaceholder.typicode.com/posts')
          .then(resp => {
            // 请求返回了数据之后,dispatch一个动作, 并且把返回的值传到这个动作所对应的reducer去处理
            dispatch({
              type: REQUEST_BLOG_SUCCESS,
              // 修改数据
              payload: {
                list: resp.data
              }
            })
          })
          .catch(err => {
            // 实际上,这里也应该去做一个dispatch,用于reducer的错误处理
            console.log(err);
          })
          .finally(()=> {
            // 请求结束之后,隐藏loading
            dispatch(hideLoading())
          })
      }
    }
    
    

    (7) src/reducers/ux.js

    import {
      HIDE_LOADING,
      SHOW_LOADING
    } from '../actions/actionType';
    
    const initialState = {
      isLoading: false
    }
    
    export default (state=initialState, action) => {
      switch(action.type) {
        case SHOW_LOADING:
          return {
            ...state,
            isLoading: true
          };
        case HIDE_LOADING:
          return {
            ...state,
            isLoading: false
          };
        default:
          return state;
      }
    }
    

    (8)src/reducers/blog.js

    import { REQUEST_BLOG_SUCCESS } from '../actions/actionType';
    const initialState = {
      list: [{
        id: 1,
        title: 'niubility',
        body: 'h5 1806'
      }]
    }
    export default (state=initialState, action) => {
      switch(action.type) {
        case REQUEST_BLOG_SUCCESS:
          return {
            ...state,
            list: action.payload.list
          };
        default:
          return state;
      }
    }
    

    (9)src/reducers/index.js

    import { combineReducers } from 'redux';
    import blog from './blog';
    import ux from './ux';
    //导出blog.js ux.js
    export default combineReducers({
      blog,
      ux
    })
    

    相关文章

      网友评论

          本文标题:23. redux

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