【问题标题】:Handling async request with React, Redux and Axios?使用 React、Redux 和 Axios 处理异步请求?
【发布时间】:2018-05-12 11:14:31
【问题描述】:

我是 React JS 和 Redux 的新手,而且太难开始了。我正在尝试使用 Axios 发出 POST 请求,但我无法做到。可能是我在容器文件中遗漏了一些东西。下面是代码。检查plnkr

更新:我在提交后收到@@redux-form/SET_SUBMIT_SUCCEEDED 消息。但是当我检查网络选项卡时,我没有看到对 API 的调用。而且当我安慰提交的值时,我只看到名称和全名值。它不包含徽标和细节。我错过了什么?

组件文件

   import React, { PureComponent } from 'react'
   import PropTypes from 'prop-types'
   import { Field,reduxForm } from 'redux-form'
   import {   Columns,Column, TextArea, Label,Button  } from 'bloomer'
   import FormField from 'FormField'

   const validate = (values) => {
     const errors = {}
    const requiredFields = 
      ['organizationName','organizationFullName','organizationDetails']

    requiredFields.forEach((field) => {
     if (!values[field]) {
     errors[field] = 'This field can\'t be empty!'
    }
  })
     return errors
}

  const formConfig = {
   validate,
   form: 'createOrganization',
   enableReinitialize: true
   }

  export class CreateOrganization extends PureComponent {
   static propTypes = {
     isLoading:PropTypes.bool.isRequired,
     handleSubmit: PropTypes.func.isRequired, // from react-redux     
     submitting: PropTypes.bool.isRequired // from react-redux
    }
   onSubmit = data => {
     console.log(data)
   }
  render () {
     const { handleSubmit,submitting,isLoading } = this.props
      return (
        <Columns isCentered>
        <form onSubmit={handleSubmit(this.onSubmit.bind(this))} > 

          <Column isSize='3/6' >        
            <Label>Organization Name</Label>             
            <Field 
              name="organizationName"
              component={FormField}
              type="text"
              placeholder="Organization Name"
            />   
          </Column>       


          <Column isSize='3/6'>
            <Label>Organization Full Name</Label>              
            <Field
              name="organizationFullName"
              component={FormField}
              type="text"
              placeholder="Organization Full Name"
            />  
          </Column> 


           <Column isSize='3/6'>            
            <Label>Organization Logo</Label>              
            <Input                  
              name="organizationLogo"                  
              type="file"
              placeholder="Logo"
            /> 
          </Column>

          <Column isSize='3/6'>
            <Label>Organization Details</Label>         
                <TextArea placeholder={'Enter Details'} />               
          </Column>          


          <Column >
            <span className="create-button">
              <Button type="submit" isLoading={submitting || isLoading} isColor='primary'>
                Submit
              </Button>  
            </span> 
              <Button type="button" isColor='danger'>
                Cancel
              </Button>                
          </Column>  

        </form>
      </Columns>
    )    
  }
}

  export default reduxForm(formConfig)(CreateOrganization)

容器文件

   import React, { PureComponent } from 'react'
   import PropTypes from 'prop-types'
   import { connect } from 'react-redux'
   import Loader from 'Loader'
   import organization from 'state/organization'
   import CreateOrganization from '../components/createOrganization'

   export class Create extends PureComponent {
   static propTypes = {    
     error: PropTypes.object,
     isLoaded: PropTypes.bool.isRequired,  
     create: PropTypes.func.isRequired,   
    }
    onSubmit = data => {
      this.props.create(data)
    }

    render () {
      const { isLoaded, error } = this.props
    return (      
       <CreateOrganization onSubmitForm={this.onSubmit} isLoading=
         {isLoading} />    
     )
   }
 }

   const mapStateToProps = state => ({
     error: organization.selectors.getError(state),
     isLoading: organization.selectors.isLoading(state)
   })

    const mapDispatchToProps = {
      create: organization.actions.create
    }


  export default connect(mapStateToProps, mapDispatchToProps)(Create)

【问题讨论】:

  • 首先,人们会开始对这个问题投反对票,纯粹是因为格式很糟糕。使其难以阅读。如果您无法在此处获取格式,您可以发布一个 codepen 示例。其次,您应该更详细地描述您的需求。什么不工作?你能进入 callAPI 函数吗?响应没有得到控制台记录吗?
  • 附注&lt;CreateOrg onSubmit={this.props} /&gt; this.props 是一个对象不是一个函数
  • 我无法进入调用 API 函数。感谢您的建议。将添加更改
  • onSubmit 应该指向 props 对象上的一个函数。
  • 那个函数会在行动中声明?

标签: reactjs redux axios redux-form


【解决方案1】:

您的 redux 操作创建者必须是普通的对象,并且应该使用强制键 type 进行调度和操作。但是使用自定义中间件,如redux-thunk,您可以在您的动作创建者中调用axios 请求,因为没有自定义middlewares,您的动作创建者需要返回普通对象

你的动作创建者看起来像

export function create (values) {

  return (dispatch) => {
     dispatch({type: CREATE_ORGANIZATION});
     axios.post('/url', values)   
        .then((res) =>{
            dispatch({type: CREATE_ORGANIZATION_SUCCESS, payload: res});
        })
        .catch((error)=> {
            dispatch({type: CREATE_ORGANIZATION_FAILURE, payload: error});
        })
  }

}

你的减速器看起来像

export default (state = initialState, action) => {
  const payload = action.payload

   switch (action.type) {    
    case CREATE:    

      return {
        ...state,
        loading: true,
        loaded: false
      }

    case CREATE_SUCCESS:
      return {
        ...state,
        data: state.data.concat(payload.data),
        loading: false,
        loaded: true,
        error: null
      }   

      }

    case CREATE_FAILURE:

      return {
        ...state,
        loading: false,
        loaded: true,
        error: payload
      }
    default:
      return state
  }
}

现在在创建商店时,您可以这样做

import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

除此之外你还需要设置redux表单

你需要使用 combineReducers 和 Provider 来传递 store

import reducer from './reducer';
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form'

export const rootReducer = combineReducers({
   reducer,
   form: formReducer
})

CodeSandbox

【讨论】:

  • 检查此答案,如果您遇到任何困难,请告诉我
  • 我仍然在控制台中收到'@@redux-form/SET_SUBMIT_FAILED'
  • 我添加了一个包含所有错误纠正的代码沙箱,您需要使用 combineReducers 并在其中设置 redux-form。除此之外,还有其他错误,例如没有为组件使用大写字符。如果您仍然遇到麻烦,请告诉我
  • 去看看。谢谢
  • 您的配置似乎不允许 redux-thunk 提交触发您的 api 请求的操作。检查你的applyMiddleware( your_middleware?, thunk, logger)(createStore) 然后尝试在不使用redux-form 的情况下实现逻辑
【解决方案2】:

你可以在 redux-saga 的帮助下轻松做到这一点。

关于redux-saga:

redux-saga 是一个库,旨在使应用程序的副作用(即数据获取之类的异步操作和访问浏览器缓存之类的不纯操作)更易于管理、执行效率更高、测试简单且更擅长处理故障.

安装:

$ npm install --save redux-saga 

$ yarn add redux-saga

请参考链接:https://github.com/redux-saga/redux-saga

【讨论】:

    【解决方案3】:

    Redux 操作创建者显然不支持异步操作,这是您尝试对发布请求执行的操作。 Redux Thunk 应该对此有所帮助。

    您需要一个如下所示的 store.js 文件:

    //npm install --save redux-thunk
    
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from './reducer.js';
    
    // Note: this API requires redux@>=3.1.0
    const store = createStore(
      rootReducer,
      applyMiddleware(thunk) //needed so you can do async
    );
    

    这是您的操作文件的外观。 Create 成为一个动作创建者,它返回一个函数,然后执行发布请求并允许您在那里进行调度,从而允许您更新您的商店/状态。 :

    import axios from 'axios'
    import { CREATE_ORGANIZATION, CREATE_ORGANIZATION_SUCCESS, CREATE_ORGANIZATION_FAILURE,
    
           } from './constants'
    import * as selectors from './selectors'
    
    /*
      CREATE ORGANIZATION
    */
    //uses redux-thunk to make the post call happen
    export function create (values) {
      return function(dispatch) {
        return axios.post('/url', values).then((response) => {
          dispatch({ type: 'Insert-constant-here'})
          console.log(response);
        })
        }
      }
    

    另外,您需要像这样将您创建的 onSubmit 方法传递到 onSubmitForm 中。我不确定 isLoading 来自哪里,因为我没有看到它在该容器组件中导入,因此您可能也想查看它。:

      <createOrganization onSubmitForm={this.onSubmit.bind(this)} isLoading={isLoading} />
    

    【讨论】:

    • 'Insert-constant-here' 是不是像 CREATE_ORGANIZATION_SUCCESS 这样的操作类型?
    • 是的,把动作类型放在那里。
    • 我仍然在控制台中收到'@@redux-form/SET_SUBMIT_FAILED'
    • 我更新了我的答案。我想你会想在那里做一些改变
    • 仍然没有调用 API
    【解决方案4】:

    我建议使用redux-promise-middleware。这个库要求动作有一个名为payload 的属性,这是一个promise,这很容易用axios。然后它与Redux 集成,以在根操作类型(例如GET_CUSTOMERS)后缀PENDINGFULFILLEDREJECTED 并触发这些操作。

    触发动作与任何其他动作相同。

    商店

    import {applyMiddleware, compose, createStore} from 'redux';
    import promiseMiddleware from 'redux-promise-middleware';
    import reducer from './reducers';
    
    let middleware = applyMiddleware(promiseMiddleware());
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const enhancer = composeEnhancers(middleware);
    
    export default createStore(reducer, enhancer);
    

    动作

    export function getCustomers() {
      return {
        type: 'GET_CUSTOMERS',
        payload: axios.get('url/to/api')
          .then(res => {
            if (!res.ok) throw new Error('An error occurred.');
            return res;
          })
          .then(res => res.json())
          .catch(err => console.warn(err));
      };
    }
    

    减速器

    export default function(state = initialState, action) => {
      switch (action.type) {
        case 'GET_CUSTOMERS_PENDING':
          // this means the call is pending in the browser and has not
          // yet returned a response
          ...
        case 'GET_CUSTOMERS_FULFILLED':
          // this means the call is successful and the response has been set
          // to action.payload
          ...
        case 'GET_CUSTOMERS_REJECTED':
          // this means the response was unsuccessful so you can handle that
          // error here
          ...
        default:
          return state;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-20
      • 2021-07-02
      • 2019-10-20
      • 1970-01-01
      相关资源
      最近更新 更多