Handling async request with React, Redux and Axios?

HebleV picture HebleV · Nov 28, 2017 · Viewed 11.3k times · Source

I am new to React JS and Redux and it has been too overwhelming to get going. I am trying to make a POST request using Axios, but I am unable to make it. May be I am missing something in the container file. Below is the code. Check plnkr

Update: I am getting @@redux-form/SET_SUBMIT_SUCCEEDED message after submitting. But when I am checking in the network tab, I don't see the call to API. And also when I am consoling the submitted values, I see only name and fullname values. It doesn't consist of logo and details. What am I missing?

Component file

   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)

Container File

   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)

Answer

Shubham Khatri picture Shubham Khatri · Dec 1, 2017

Your redux action creators must be plain, object and should dispatch and action with a mandatory key type. However using custom middlewares like redux-thunk you could call axios request within your action creators as without custom middlewares your action creators need to return plain object

Your action creator will look like

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});
        })
  }

}

and your reducer will look like

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
  }
}

now while creating the store you can do it like

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

Apart from this you also need to setUp the redux form

you need to use combineReducers and Provider to pass on the store

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

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

CodeSandbox