How to pass Dispatch to action when using redux form

9.8k views Asked by At

I have the following Search Component

import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';

import { search } from '../../actions/actions'

class Search extends Component {

    render() {
        const {handleSubmit, "fields":{query}} = this.props;

        return (
            <div>
                <form onSubmit={handleSubmit(search)}>
                    <Field className='searchInput' name="query" component="input" type="text" placeholder="Enter course name"/>
                    <button className='searchSubmit' type='submit' >Search</button>
                </form>
            </div>
        );
    }
}
export default reduxForm({
    "form":"searchForm",
    "fields":["query"]
}, null, {search})(Search);

My search Action is like so

search : function(query){
    var data = {
        ep:"EP_SEARCH",
        payload:{
            query: query.query
        }
    }

    getAsynch(data).then(function(result){
        return {type: GET_DATA, payload:result};
    })
}

I get a response from the endpoint with a result but I need to somehow dispatch the action.

I tried instead of

getAsynch(data).then(function(result){
            return {type: GET_DATA, payload:result};
        })

this

return function(dispatch){
    getAsynch(data).then(function(result){
        dispatch({type: GET_DATA, payload:result});
    })
}

but it throws an error that dispatch is not defined. Probably because I'm not passing it anywhere in the Component.

3

There are 3 answers

0
Ando On BEST ANSWER

I'm using Redux Thunk, and my actions work properly for everything except ReduxForm. After reading a lot on the topic if discovered the solution.

search : function(query, dispatch){ //APPARENTLY DISPATCH IS THE SECOND PARAMETER, WHICH WAS MISSING IN MY INITIAL FUNCTION
        var data = {
            ep:"EP_SEARCH",
            payload:{
                query: query.query
            }
        }
        getAsynch(data).then((result)=>{
            dispatch({ type: GET_DATA, payload:result })
        });
    }

More info on the topic

onSubmit : Function [optional]

The function to call with the form data when the handleSubmit() is fired from within the form component. If you do not specify it as a prop here, you must pass it as a parameter to handleSubmit() inside your form component.

If your onSubmit function returns a promise, the submitting property will be set to true until the promise has been resolved or rejected. If it is rejected with a redux-form SubmissionError containing errors in the form { field1: 'error', field2: 'error' } then the submission errors will be added to each field (to the error prop) just like async validation errors are. If there is an error that is not specific to any field, but applicable to the entire form, you may pass that as if it were the error for a field called _error, and it will be given as the error prop.

onSubmit will be called with the following parameters:

values : Object

The field values in the form of { field1: 'value1', field2: 'value2' }.

dispatch : Function

The Redux dispatch function.

props : Object

The props passed into your decorated component.

http://redux-form.com/6.2.0/docs/api/ReduxForm.md/

TBH I still don't get how dispatch got inside the action creator and why the previous suggestions, which I tried, do not work. A comment from someone more familiar with the topic would be nice.

1
therewillbecode On

Since this is an asynchronous action you will want to use middleware in order to handle the dispatching of multiple actions at different times.

Actions creators are synchronous by nature so this is why middleware is necessary in order to dispatch synchronous actions in the future when the response comes back from your request.

In order to get to grips I would recommend you use redux-thunk as is easier to get to grips with than the main alternative redux-promise middleware library.

Redux Thunk Middleware

Redux Thunk middleware allows you to write action creators that return a function instead of an action.

First set up your redux-thunk middleware during your store configuration

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

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

Now redux-thunk knows how to handle the dispatching of special thunk actions

A thunk actions is just an action that returns another function that takes dispatch as an argument.

Thunk action creator

    function fetchThing(url) { 
        return (dispatch) => {
             /*dispatch more actions*/
        }
    }

The advantage of this is that our components can dispatch asynchronous actions as if they were synchronous and do not need to be passed the dispatch function and therefore don't need to know about the Redux store.

The redux-thunk middleware knows to intercept our thunk action just after we create it, passing in dispatch so future actions such as a RESPONSE_RECEIVED action will have access to dispatch.

In order to use redux-thunk create an action creator which returns a function that accepts dispatch as an argument as per the above.

Example

function epSearch(query){
    // thunk passes dispatch into the function below 
    // so that future actions will have access to dispatch

    return function(dispatch){

         // below function needs to return a promise in order for us to call .then
        getAsynch(data).then(   
          result => dispatch({ type: GET_DATA, payload:result }),
          error dispatch({ type: REQUEST_ERROR, payload: error })
        );
    };
}

If the promise is not resolved due to an error than our REQUEST_ERROR action will be dispatched and the payload of the action will be the error we received from the rejected promise

Redux Promise and Alternatives to Redux Thunk

Now there are other ways of controlling your async data flow. However redux-thunk is simple and convenience in my opinion. Redux Promise is a a good alternative to Redux Thunk. The difference here is that redux-promise is middleware that intercepts action creators that return promises instead of thunks.

However strictly speaking you don't need middleware at all in redux for async flow. The advantage of it is the centralisation of data flow in one place that allows your components to be more decoupled as they do not need to be explicitly passed the Redux's store's dispatch method and therefore do not care about how and action is created.

For an in depth discussion on middleware in Redux see this answer by Dan Abramov's to the question of why do we need middleware for async flow in redux,

0
Jye Nicolson On

TBH I still don't get how dispatch got inside the action creator and why the previous suggestions, which I tried, do not work. A comment from someone more familiar with the topic would be nice.

(apologies, not enough rep for a comment)

redux-form's handleSubmit appears to always pass dispatch (as derived from its own props parameter) as the second parameter to the submit function you provide (see https://github.com/erikras/redux-form/blob/master/src/handleSubmit.js)

I would assume this is normally undefined, but thunk, if present, will make dispatch available to redux-form and thus to you.