【问题标题】:How to make AJAX request in redux如何在 redux 中发出 AJAX 请求
【发布时间】:2015-11-24 10:53:06
【问题描述】:

据我所知,我必须在创建操作中编写请求。如何在行动中使用承诺来提交请求?我正在获取数据。然后在 reducer 中创建新状态。在 connect 中绑定 action 和 reducer。但是我不知道如何使用promise来请求。

动作

import $ from 'jquery';
export const GET_BOOK = 'GET_BOOK';

export default function getBook() {
  return {
    type: GET_BOOK,
    data: $.ajax({
      method: "GET",
      url: "/api/data",
      dataType: "json"
    }).success(function(data){
      return data;
    })
  };
}

减速器

import {GET_BOOK} from '../actions/books';

const booksReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_BOOK:
      return state;
    default:
      return state;
  }
};

export default booksReducer;

容器 如何在容器中显示数据?

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import getBook  from '../actions/books';
import Radium from 'radium';
import {Link} from 'react-router';

function mapStateToProps(state) {
  return {
    books: state.data.books,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    getBooks: () => dispatch(getBook()),
  };
}

@Radium
@connect(mapStateToProps, mapDispatchToProps)
class booksPage extends Component {
  static propTypes = {
    getBooks: PropTypes.func.isRequired,
    books: PropTypes.array.isRequired,
  };

  render() {
    const {books} = this.props;
    return (
      <div>
        <Link to={`/authors`}><MUIButton style="flat">All Authors</MUIButton></Link>
        <ul>
          {books.map((book, index) =>
            <li key={index}>
              <Link to={`/book/${book.name}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
                "{book.name}"</div></MUIButton></Link>
              <Link to={`/author/${book.author}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
                {book.author}</div></MUIButton></Link>
            </li>
          )}
        </ul>
      </div>
    );
  }
}

export default booksPage;

【问题讨论】:

    标签: ajax reactjs request redux


    【解决方案1】:

    由于您已经在使用 redux,您可以应用 redux-thunk 中间件,它允许您定义异步操作。

    安装使用:Redux-thunk

    export function fetchBook(id) {
     return dispatch => {
       dispatch(setLoadingBookState()); // Show a loading spinner
       fetch(`/book/${id}`, (response) => {
         dispatch(doneFetchingBook()); // Hide loading spinner
         if(response.status == 200){
           dispatch(setBook(response.json)); // Use a normal function to set the received state
         }else { 
           dispatch(someError)
         }
       })
     }
    }
    
    function setBook(data) {
     return { type: 'SET_BOOK', data: data };
    }
    

    【讨论】:

      【解决方案2】:

      你应该使用Async Actions described in Redux Documentation

      这里是异步操作的减速器示例。

      const booksReducer = (state = {}, action) => {
        switch (action.type) {
          case 'RESOLVED_GET_BOOK':
            return action.data;
          default:
            return state;
        }
      };
      
      export default booksReducer;
      

      然后你创建你的异步操作。

      export const getBook() {
        return fetch('/api/data')
          .then(response => response.json())
          .then(json => dispatch(resolvedGetBook(json)))
      }
      
      export const resolvedGetBook(data) {
        return {
          type: 'RESOLVED_GET_BOOK',
          data: data
        }
      }
      

      几个注意事项:

      • 我们可以使用 redux-thunk 中间件返回 Promise(而不是 Object)。
      • 不要使用 jQuery ajax 库。使用其他专门用于执行此操作的库(例如 fetch())。我用axios http client
      • 请记住,在 redux 中,您只能在 reducer 中使用纯函数。不要在 reducer 中进行 ajax 调用。
      • 阅读 redux 文档中的完整指南。

      【讨论】:

        【解决方案3】:

        您应该能够在回调中使用dispatch(如果您将其作为参数传递):

        export default function getBook(dispatch) {
          $.ajax({
              method: "GET",
              url: "/api/data",
              dataType: "json"
            }).success(function(data){
              return dispatch({type:'GET_BOOK', data: data});
            });
        }
        

        然后,将dispatch 传递给操作:

        function mapDispatchToProps(dispatch) {
          return {
            getBooks: () => getBook(dispatch),
          };
        }
        

        现在,您应该可以访问 reducer 中的 action.data 属性:

        const booksReducer = (state = initialState, action) => {
          switch (action.type) {
            case GET_BOOK:
              //action.data  <--- here
              return state;
            default:
              return state;
          }
        };
        

        【讨论】:

        • 谢谢,但现在我收到警告:道具类型失败:booksPage 中未指定必需的道具books。检查Connect(booksPage).warning @ (program):45 (program):45 的渲染方法警告:getDefaultProps 仅用于经典的 React.createClass 定义。请改用名为 defaultProps 的静态属性。
        • 你把action.data 减少到状态了吗?
        • Object.assign({}, state, { books: action.data.books, authors: action.data.authors });
        • 如果你这样减少,那么在你的mapStateToProps 中找不到state.data.books。在那里做一个console.log 看看state 是什么。
        • 我使用了 console.log,state.data 等于 0。这意味着 GET_BOOK 案例不起作用。但为什么呢?
        【解决方案4】:

        您可能希望分离关注点,以保持动作创建者“纯粹”。

        解决方案;写一些中间件。以此为例(使用超级代理)。

        import Request from 'superagent';
        
        const successHandler = (store,action,data) => {
        
            const options = action.agent;
            const dispatchObject = {};
            dispatchObject.type = action.type + '_SUCCESS';
            dispatchObject[options.resourceName || 'data'] = data;
            store.dispatch(dispatchObject);
        };
        
        const errorHandler = (store,action,err) => {
        
            store.dispatch({
                type: action.type + '_ERROR',
                error: err
            });
        };
        
        const request = (store,action) => {
        
            const options = action.agent;
            const { user } = store.getState().auth;
            let method = Request[options.method];
        
            method = method.call(undefined, options.url)
        
            if (user && user.get('token')) {
                // This example uses jwt token
                method = method.set('Authorization', 'Bearer ' + user.get('token'));
            }
        
            method.send(options.params)
            .end( (err,response) => {
                if (err) {
                    return errorHandler(store,action,err);
                }
                successHandler(store,action,response.body);
            });
        };
        
        export const reduxAgentMiddleware = store => next => action => {
        
            const { agent } = action;
        
            if (agent) {
                request(store, action);
            }
            return next(action);
        };
        

        将所有这些放在一个模块中。

        现在,您可能有一个名为“auth”的动作创建者:

        export const auth = (username,password) => {
        
            return {
                type: 'AUTHENTICATE',
                agent: {
                    url: '/auth',
                    method: 'post',
                    resourceName: 'user',
                    params: {
                        username,
                        password
                    }
                }
            };
        };
        

        中间件将获取属性“代理”,通过网络发送构造的请求,然后将传入的结果分派到您的商店。

        在你定义钩子之后,你的 reducer 会处理所有这些:

        import { Record } from 'immutable';
        
        const initialState = Record({
            user: null,
            error: null
        })();
        
        export default function auth(state = initialState, action) {
        
            switch (action.type) {
        
                case 'AUTHENTICATE':
        
                    return state;
        
                case 'AUTHENTICATE_SUCCESS':
        
                    return state.merge({ user: action.user, error: null });
        
                case 'AUTHENTICATE_ERROR':
        
                    return state.merge({ user: null, error: action.error });
        
                default:
        
                    return state;
            }
        };
        

        现在将所有这些注入到您的视图逻辑中。我以 react 为例。

        import React from 'react';
        import ReactDOM from 'react-dom';
        
        /* Redux + React utils */
        import { createStore, applyMiddleware, bindActionCreators } from 'redux';
        import { Provider, connect } from 'react-redux';
        
        // thunk is needed for returning functions instead 
        // of plain objects in your actions.
        import thunkMiddleware from 'redux-thunk';
        
        // the logger middleware is useful for inspecting data flow
        import createLogger from 'redux-logger';
        
        // Here, your new vital middleware is imported
        import { myNetMiddleware } from '<your written middleware>';
        
        /* vanilla index component */
        import _Index from './components';
        
        /* Redux reducers */
        import reducers from './reducers';
        
        /* Redux actions*/
        import actionCreators from './actions/auth';
        
        
        /* create store */
        const store = createStore(
            reducers,
            applyMiddleware(
                thunkMiddleware,
                myNetMiddleware
            )
        );
        
        /* Taint that component with store and actions */
        /* If all goes well props should have 'auth', after we are done */
        const Index = connect( (state) => {
        
            const { auth } = state;
        
            return {
                auth
            };
        }, (dispatch) => {
        
            return bindActionCreators(actionCreators, dispatch);
        })(_Index);
        
        const provider = (
            <Provider store={store}>
                <Index />
            </Provider>
        );
        
        const entryElement = document.getElementById('app');
        ReactDOM.render(provider, entryElement);
        

        所有这一切都意味着您已经使用 webpack、rollup 或其他东西设置了一个管道,以从 es2015 转换并做出响应,以响应 vanilla js。

        【讨论】:

          【解决方案5】:

          考虑使用新的 thunk API

          export const load = createAsyncThunk(
            'example/api',
            async (arg, thunkApi) => {
              const response = await fetch('http://example.api.com/api')
          
              if (response.status === 200) {
                const json = await response.json()
                return json
            },
          )
          

          另外,在新的 redux 模板应用程序中,action 是 reducer/slice 的一部分,您可以使用 extraReducers 来响应与异步 action 状态相关的事件。这样使用 redux 就简单多了。

          在此处查看异步 thunk 的文档:https://redux.js.org/usage/writing-logic-thunks

          【讨论】:

            猜你喜欢
            • 2017-02-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-05
            • 2015-06-27
            • 2017-11-30
            相关资源
            最近更新 更多