Redux: How to call actions without using @connect?

Created on 30 Jul 2015  ·  32Comments  ·  Source: reduxjs/redux

I have a header container which passes image data (url, size) down to a header component using

@connect(state => ({
  header: state.header
}))
export default class HeaderContainer extends Component {
 /// pass props.header data to child Header which renders image.
}

Then I have another component which needs to call header actions in order to update the header state. It doesn't use the state, it just fires actions. Afaik I need to do something like this:

const { dispatch } = this.props
const actions = bindActionCreators(headerActions, dispatch)

The problem is, this.props.dispatch only seems to be available when you use the @connect decorator. The second component is not interested in the header state at all. Do I really need to use connect here or can it somehow bind the actions in a different way? It feels wrong to use @connect if I don't use the data.

question

Most helpful comment

You can pass dispatch down to that component manually as a prop.

All 32 comments

I'm not sure is it a "right" way or not, but you can access the store through the context. See the Connector component for example:
https://github.com/gaearon/react-redux/blob/master/src/components/createConnector.js

I don't follow you. I am perfectly fine accessing the store by using @connect. I am looking for a way to bind/call actions in a component without using @connect.

The dispatch function is a method of the store. If you need it in your component but do not want to use @connect, then you can access the store through the context and use its dispatch method:

class MyComponent extends Compoenent {
    static contextTypes = {
        store: React.PropTypes.object.isRequired,
    }

    render() {
         const { dispatch } = this.context.store
         const actions = bindActionCreators(headerActions, dispatch)
    }
}

You should pass the dispatch down from the parent component, or the action already bound to the dispatch. That is _the_ way I believe, context also works, albeit it's a workaround

Thanks for explaining this! I get it now.

You can pass dispatch down to that component manually as a prop.

I am a bit puzzled by this issue too.

If I understand correctly, we need either to have dispatch available through props of a component (to be able to dispatch actions from that component) or to give action creators already bound to dispatch, from a parent component. But both solutions require to have a parent component that is connected so that dispatch is made available to it via its props.

So in the end we're just transferring our dependency of dispatch onto a parent component. And how would you do that if there is no such connected parent?

For example, what if we were in a deep hierarchy of components:

Provider
  Router
    AppContainer
     ...
       ...
         ...
           TheComponentThatNeedDispatch

and if none of the parents needs to connect to the redux state?

Having dispatch made available to a component as a side-effect of using connect seems to put us in a situation where it is a pre-requisite to have one its parent to be connected to be able to dispatch an action. This feels weird since there should not be any correlation between the ability of a component to dispatch an action and the need to use connect to read from redux state.

I know that we could still access dispatch from a component context (via store.dispatch) but since it's not the recommended way...

What do you think about it?

Thanks!

This feels weird since there should not be any correlation between the ability of a component to dispatch an action and the need to use connect to read from redux state.

Why is it weird? Think of connect as “connect to Redux” and not just “connect to a part of Redux state”. Then it makes sense: to be able to dispatch either component itself, or some its parent, needs to be connect()ed to Redux.

With current React Redux API it's not convenient to connect to Redux without also selecting state. You'd have to write something like connect(state => ({}))(MyComponent) (return empty object). This is why we were exploring alternative APIs for it that make dispatch first-class citizen and plan to add an API for grabbing “just the dispatch”.

Does this make sense?

Think of connect as “connect to Redux” and not just “connect to a part of Redux state”.

Totally makes sense now :).

Can't wait to use the new API.

Thanks a lot, using Redux is a bliss!

This looks great!

@gaearon hey Dan can you give me a tip on how to use bindActionCreators with nested components:

"AddProduct" - connected to redux (see below)
__ "ProductsList"
____"ProductView".

I want ProductView to be able to dispatch, but only the master "Add Product" has:

function appState(state) {
    return {...};
}

export default connect(appState)(AddProduct)

You can either connect() the nested component too, or you can pass dispatch to it as a prop.

thanks Dan, really appreciate your help. really love redux too!

another question if i may:
can i place server API post requests from inside the reducer itself?
when i POST to the database (mongodb), the item gets an "_id" that i would like to have in my redux state. should the dispatch be placed as a callback for the POST request?

thank you

can i place server API post requests from inside the reducer itself?

Definitely not, reducers must be pure functions free of any side effects.

when i POST to the database (mongodb), the item gets an "_id" that i would like to have in my redux state. should the dispatch be placed as a callback for the POST request?

Yes.

Please see this guide: http://rackt.github.io/redux/docs/advanced/AsyncActions.html

thanks Dan, works great (y)

if im using server rendering for lets say getting the current user logged in,
can i skip the async fetch from the store, and do it on the server?

if im using server rendering for lets say getting the current user logged in, can i skip the async fetch from the store, and do it on the server?

You can create store on the server, wait for async actions to finish, and then render.
See also http://rackt.github.io/redux/docs/recipes/ServerRendering.html

I'm probably missing something critical, but why not just partially-apply dispatch onto all the action-creators during app initialization? Then you could call then from anywhere without worrying about how to get a reference to dispatch.

@mpoisot : because you'd have to know, import, bind, and distribute _every_ potential action creator at app startup. Among other things, that doesn't help with situations like code splitting.

@markerikson I already have to know all the reducers at app init so I can call createStore. I'm imagining calling bindAllActionCreators(store.dispatch) on the next line.

True, you might end up pulling in slightly more code than you actually use. But if that's a concern then don't do it this way. Or break up your reducer-sets (reducer + action creators + selectors) into smaller, more focused bundles. Or only bind some action creators.

Is there some terrible performance penalty for doing this? Or it somehow leads down a terrible path to coding hell?

@mpoisot : not really any perf problems I can think of. However, it does limit testability and reusability.

Proper use of connect results in a light Dependency Injection approach. A "plain" component only knows that it's being given some function as a prop, and it should call that function as needed. It doesn't actually care if it's a callback that was passed down directly by some parent component, or a pre-bound action creator. That means that the component can be reused in a straightforward way, and also makes testing it a lot easier. On the other hand, doing something like directly importing the store results in tying the component to that specific store instance and implementation, which is why it's discouraged (per the Redux FAQ: http://redux.js.org/docs/faq/StoreSetup.html#store-setup-multiple-stores ). If I understand what you're asking about correctly, it's basically the same situation as directly importing and referencing the store.

Ah good point, partially-applying dispatch in action creators would be equivalent to importing the store as a singleton, which is frowned upon. I tried to learn why, and I found several places where Dan Abramov said a singleton store is frowned upon due to server side rendering (which most people will never use), but strange he never mentioned testing (which everyone should do, or at least feel bad about not doing).

Which makes me think, surely you can create a store singleton object whose store can be updated with different stores for testing? Instead of directly exporting the store itself, return it via StoreSingleton.getStore(), and change the current store via StoreSingleton.setStore(). So in terms of testing you wouldn't be tied to a single instance of a store. However, I can see why this wouldn't work in a threaded server environment.

@mpoisot You just described what Provider/connect do. ;)

Often I have a "dumb" component that one day needs dispatch (or an pre-bound action). So I wrap him in connect, or maybe pass stuff down from a containing component that's connected. It often feels like a lot of wiring and passing props just to avoid globally accessible dispatch. For mapStateToProps() there are wonderful performance gains with Provider/connect(). And for reusing dumb components it definitely makes sense extract dispatch logic to the higher order component.

But I've hit this scenario enough times to wonder why. And it appears the answer is "so you can do server side rendering someday". (Please correct me if I'm wrong).

Right now, connect() just gives you the dispatch method from Provider's store, but in the future, maybe that could change. We might add extra functionality, such as integration with dev tools to tell you what React component was the source of an action that's firing, for example. Or someone might make a component that enhances the store in another way by taking the store in context, enhancing it somehow, and then passing that enhanced store down the component tree. If you've bound your action creators to a singleton store, you'd potentially be calling the wrong dispatch.

Yep, some of the "component/encapsulated state" experiments I've seen work with exactly that approach. The nifty thing about that idea is that the other components don't need to know anything's changed, since the wrapper components are still just grabbing this.context.store and using its dispatch as needed.

I've been on both sides of this --

In my primary application we treat the store as a singleton and use a globally available getStore() function. We don't use server-side rendering. We still use Provider and connect for mapStateToProps.

On the other hand, I've built an experimental app where I wrap the store (like @jimbolla and @markerikson mentioned) in various places within the component hierarchy, and both dispatch and getState can change throughout the app, which means they _have_ to be wired throughout the app (either via context or props) and can't be singletons.

Depending on what you want to accomplish, I think the singleton store stuff is viable, but personally I'm slowly moving away from it.

Yeah. You certainly _can_ treat the store as an accessible singleton, but it does limit you, it's not considered idiomatic, and I don't see any real advantage to doing so.

Thanks, great feedback. Can someone post a link showing off some store-wrapping?

@mpoisot

Here is one of Dan's comments that illustrates how wrapping dispatch could be useful:

https://github.com/reactjs/redux/issues/822#issuecomment-145271384

You might have to read the whole thread to better understand what the motivation is.

In particular, notice how dispatch is wrapped (I've highlighted it):

wrappeddispatch

Sorry for hijacking this old post as im facing the similar confusion, wishing to dispatch action without connect.

export default class App extends Component {
...
  componentDidMount() {
    //registering event listener
    BackgroundGeolocation.on('location', this.onLocation, this.onError);
  }

  onLocation(location) {
   //wishing to dispatch an action to update variable in store
  }

  render() {
    return (
      <Provider store={store}>
        <AlertProvider>
          <MainNavigator screenProps={{ isConnected: this.state.isConnected }} />
        </AlertProvider>
      </Provider>
    );
  }
}

From what I understand, I can't possibly connect to store in this component as we are just configuring and pass the store into Provider. How would i possibly dispatch an action in my onLocation event?

@isaaclem : well, in that specific case, you _do_ have a reference to the store, so you could just directly call store.dispatch(someAction).

The other option would be to restructure your component structure so that some component _inside_ the <Provider> does the background geolocation behavior, and could also be connected.

Was this page helpful?
0 / 5 - 0 ratings