3
\$\begingroup\$

Let's say I have following action:

export function signIn(data) {
 return {
 type: USER_SIGN_IN,
 promise: api.post('/sign_in', data)
 }
}

and the following middleware:

export default function promiseMiddleware() {
 return next => action => {
 const { promise, type, ...rest } = action
 if (!promise) {
 return next(action)
 }
 const SUCCESS = `${type}_SUCCESS`
 const REQUEST = `${type}_REQUEST`
 const ERROR = `${type}_ERROR`
 next({ ...rest, type: REQUEST })
 return promise
 .then(res => {
 next({ ...rest, response: res.data, type: SUCCESS })
 return true
 })
 .catch(error => {
 next({ ...rest, response: error.response.data, type: ERROR })
 return false
 })
 }
}

This code is loosely based on https://github.com/reactGo/reactGo/

But what if in then callback after calling next I want to make a redirect to another path?

I did following. I passed redirect url through action:

export function signIn(data) {
 return {
 type: USER_SIGN_IN,
 promise: api.post('/sign_in', data),
 redirect: '/'
 }
}

and added another call of next method with push from react-router-redux:

import { push } from 'react-router-redux'
export default function promiseMiddleware() {
 return next => action => {
 const { promise, type, redirect, ...rest } = action
 if (!promise) {
 return next(action)
 }
 const SUCCESS = `${type}_SUCCESS`
 const REQUEST = `${type}_REQUEST`
 const ERROR = `${type}_ERROR`
 next({ ...rest, type: REQUEST })
 return promise
 .then(res => {
 next({ ...rest, response: res.data, type: SUCCESS })
 next(push(redirect))
 return true
 })
 .catch(error => {
 next({ ...rest, response: error.response.data, type: ERROR })
 return false
 })
 }
}

It seems like it works, but I'm not sure if this is a good idea or if there are some pitfalls of multiple next calls and I shouldn't do like this? Or maybe I should use dispatch method which I can get from promiseMiddleware params instead of next for redirecting?

If there are some better approaches for implementing such redirects, please, let me know.

Phrancis
20.5k6 gold badges69 silver badges155 bronze badges
asked Aug 9, 2016 at 21:30
\$\endgroup\$
0

2 Answers 2

1
\$\begingroup\$

Instead of creating your custom promiseMiddleware you could use the redux-thunk middleware which accepts a function as an action. And the function will be called with dispatch and getStore as parameters.

Imagine you would like to fetch some data.. and you want to do it as an action. like dispatch(getPost(id))

normally your actionCreator getPost would return the action object,

const getPost = (id) => {
 return {
 type: "FETCH_POST",
 id
 }
}

But when you use redux-thunk you can return a function:

const getPost = (postId) => {
 return dispatch => {
 // Now you can use dispatch inside this block, even for async calls..
 // Start fetching.. 
 dispatch({type: "START_LOADING_POST" }) // imagine this could change your state to {isLoading: true}
 http
 .get("from.somewhere.net/post/" + postId)
 .then((data) => dispatch({type: "POST_LOADED_SUCCESS", postId, data }))
 .catch((error) => dispatch(loadingPostFailed({postId, error})))
 }
}

I think you know how to apply the middleware to the store, so I skip it. Hope it makes it clear how you can extend your actionCreators with redux-thunk to functions and use them specially for async tasks, like promises or even with callback-based libraries.

Redux thunk was part of redux, but its now an own package, but also maintained by Dan Abramov, check out the repo: https://github.com/gaearon/redux-thunk

If you would like to save some time by writing every actionCreator by hand you could create an creator for the actionCreator :D sounds funny, but take a look at this:

const promiseActionCreator = type => {
 const
 start = type + "_START",
 resolved = type + "_RESOLVED",
 failed = type + "_FAILED";
 return (params) => (dispatch, getState) => {
 dispatch({type: start})
 http
 .get(params.url)
 .then(result => dispatch({type: resolved, result }))
 .catch(error => dispatch({type: failed, error }))
 }
}
// Now you can create/generate your special promise based actionCreator
const getPost = promiseActionCreator("POST_LOADER");
answered Aug 9, 2016 at 22:27
\$\endgroup\$
8
  • 1
    \$\begingroup\$ I got it, thanks, but there will be a lot of boilerplate and repeating code in actionCreators which I'd like to avoid and make my actions small and clean. That's why I were looking for better approach if it exists. \$\endgroup\$ Commented Aug 10, 2016 at 5:22
  • \$\begingroup\$ common which code will repeat that much? you have to create the promise, you have to call then / catch anyway.. \$\endgroup\$ Commented Aug 10, 2016 at 9:52
  • \$\begingroup\$ This approach handles the promise inside the action, your approach is to create the promise and pass it via the action.., right? \$\endgroup\$ Commented Aug 10, 2016 at 9:54
  • \$\begingroup\$ In my approach for each actionCreator I create promise and pass it to the action with some additional params if needed (e.g. redirect path, flash message etc.), and then I don't have to write promise handlers in each actionCreator (since all of them will be pretty similar). Does it make sense? \$\endgroup\$ Commented Aug 10, 2016 at 11:46
  • 1
    \$\begingroup\$ OK, but what if I get 404 from API? Should I handle request fail in every actionCreator, check its status and redirect to 404 page? I think such things definitely should be handled somewhere in one single place... \$\endgroup\$ Commented Aug 10, 2016 at 15:01
1
\$\begingroup\$

In the case that you want to stop dispatching process and start over with new action (redirecting action), it's ok to use dispatch(action) instead of next(action). Because when you call dispatch(action), it will open a new cycle for middlewares.

That means action will be handled from the very first middleware towards that I think it should be. But carefully because when you call dispatch(action) inside a middleware, that can lead to infinitive loop on dispatch process. I refer you to read this article to understand more about Redux middleware.

Sᴀᴍ Onᴇᴌᴀ
29.6k16 gold badges45 silver badges203 bronze badges
answered Apr 8, 2018 at 6:49
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.