redux-utilities / redux-promise

FSA-compliant promise middleware for Redux.
MIT License
2.67k stars 134 forks source link

question: idiomatic way to reference dispatcher and/or state? #20

Open tony-kerz opened 8 years ago

tony-kerz commented 8 years ago

hi andrew,

redux-thunk facilitates referencing dispatcher and state by automatically providing the dispatcher and getState methods.

how would you recommend gaining similar references using redux-promise?

regards, tony

kevana commented 8 years ago

From this comment in July, using redux-thunk and redux-promise together looks like a pretty good pattern:

// Relies on both thunk and promise middleware
createAction('FETCH_SOMETHING', id => {
  // Thunk will be handled by thunk middleware
  return dispatch => {
    // Trigger optimistic update
    dispatch({ type: 'FETCH_SOMETHING_BEGIN', body: { id } });

    // Promise will be handled by promise middleware
    return api.fetch(id);
  }
});
tony-kerz commented 8 years ago

thanks @kevana, i can get with that.

it seems like it might be an improvement to have those characteristics in a single piece of middleware, but for now it could qualify as "idiomatic" ;)

i'll leave this open as a placeholder for the idea of converging those middleware, but project owners can feel free to close if they feel this is a sufficient solution.

tony-kerz commented 8 years ago

@kevana, i just tried the strategy outlined above, and the behavior i witnessed was the thunk not being executed.

the following doesn't take i think because createAction puts the 'thunk' in a payload field...?

export const setSkill = createAction(
  actions.SET_SKILL,
  (skill) => {
    return (dispatch, getState) => {
      dispatch(setSkillBegin(skill))
      return getArticlesPromise(skill)
    }
  }
)

i have however had some luck with something like:

export const setSkill = (skill) => {
  return (dispatch, getState) => {
    dispatch(setSkillBegin(skill))
    dispatch(createAction(actions.SET_SKILL, getArticlesPromise)(skill))
  }
}
kevana commented 8 years ago

I don't have a working example, just stumbled across that comment and figured it might be helpful

davibe commented 8 years ago

I do this

// Actions.js
import store from '../store';
import progress from './Progress/actions';

const myActionCreator = async (argument, dispatch = store.dispatch, getState = store.getState) => {
    dispatch(progress.start());
    const result = await doSomethingAsync();
    dispatch(progress.stop());
    return {
        type: 'MY_ACTION', payload: result
    };
}

This way i can call the function with just the argument from the components, but I can also specify them explicitly in my tests.

I don't use redux-actions. I just use plain redux connect() to bind and connect my action creators.

davibe commented 7 years ago

Now that i am trying to render things server-side it looks like my way of importing the store directly is bad because on the serverside i must be able to change it at runtime from one request to another. What's the correct approach here ?

insidewhy commented 7 years ago

The best way of dealing with this is to use an alternate thunk middleware that looks for the function in payload. The thunk middleware is only 5 lines of js, trivial to replace. In mine I also dispatch the action with the return value of the thunk, this is great when you combine with promise middleware.

davibe commented 7 years ago

Lately i ended up removing the promised middleware and using just redux-thunk alone. Looks like a thunk can return a promised value and if it does the dispatch() return value itself is a promise that you can await.

const actionCreator (params) => {
  return async (dispatch, getState) => { ... }
}

await dispatch(actionCreator({...}))
insidewhy commented 7 years ago

@davibe then you have an async reducer? I published redux-thunktions middleware that lets you handle this situation elegantly, there are some examples in the readme.

davibe commented 7 years ago

I don't have an async reducer. Just the code i have written above works fine with redux-thunk. Inside that async method i can dispatch other async action but at some point the final actions actually modifying the state will be sync.

insidewhy commented 7 years ago

I see, but then you'll need boilerplate each time to handle promises rejections etc. It's better to use promise middleware to handle that so you have a consistent and terse way of handling asynchronous payloads.

davibe commented 7 years ago

I am not sure what you mean. I have a "client action creator" that can dispatch success/error depending on the outcome of the api call. I use it in many other actions so I am no really repeating that code.