问题
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)
回答1:
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
回答2:
You can do that easily with the help of redux-saga.
About redux-saga:
redux-saga
is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, simple to test, and better at handling failures.
Installation:
$ npm install --save redux-saga
or
$ yarn add redux-saga
Please refer to the link : https://github.com/redux-saga/redux-saga
回答3:
Redux action creators apparently don't support asynchronous actions which is what you're trying to do with the post request. Redux Thunk should help with this.
You'll want a store.js file that looks like this:
//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
);
Here is what your actions file would look like. Create becomes an action creator that returns a function that then performs the post request and allows you to do the dispatch there allowing you to update your store/state. :
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);
})
}
}
Also, you'll want to pass in the onSubmit method you created into onSubmitForm like this. I'm not sure where isLoading is coming from because I don't see it imported in that container component so you may want to look at that too.:
<createOrganization onSubmitForm={this.onSubmit.bind(this)} isLoading={isLoading} />
回答4:
I would suggest using redux-promise-middleware. This library requires that the action have a property named payload
that is a promise and this is easy with axios
. It then integrates with Redux
to suffix the root action type (e.g. GET_CUSTOMERS
) with PENDING
, FULFILLED
, and REJECTED
and fires those actions.
Firing the action is the same as any other action.
Store
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);
Action
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));
};
}
Reducer
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;
}
}
来源:https://stackoverflow.com/questions/47541032/handling-async-request-with-react-redux-and-axios