Access the Redux Store Outside a React Component

By Dave Ceddia Comment

Need to access your Redux store outside a React component?

It’s a bit ironic, on some level… a global store of data, but no way to access it!

The React Redux connect function works great for regular React components, but if you need access to the Redux store in a plain function, the technique is a bit different.

In the examples below I’ll show how to access a JWT token from the Redux store, but the techniques will work with any data you might have.

Option 1: Export the Store

This is probably the easiest option, but it has one big caveat:

DON’T USE THIS METHOD WITH SERVER SIDE RENDERING

If your app is using SSR, and you do this, you’ll end up with a SINGLE store for all of your users. Almost definitely not what you want.

So, let’s say you have a JWT token in the store, and you want to access it from your api file. Here’s how you can do it.

Move your store creation code into its own file. store.js is a nice name.

store.js
import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

Here you are creating the store as you usually would, but then you are also exporting it. This will make it available to other files.

This will work no matter how complex your createStore call is. Feel free to glom on some middleware like thunks or sagas, devtools, and whatever else you need. The key here is to export the store.

Then, where you need access to the data, import the store. Here we’ll see an api file making a call where we need to pass a JWT token to the server:

api.js
import store from './store';

export function getProtectedThing() {
  // grab current state
  const state = store.getState();

  // get the JWT token out of it
  // (obviously depends on how your store is structured)
  const authToken = state.currentUser.token;

  // Pass the token to the server
  return fetch('/user/thing', {
    method: 'GET',
    headers: {
      Authorization: `Bearer ${authToken}`
    }
  }).then(res => res.json());
}

The Redux FAQ has an entry on Can I import my store directly, and use it in components myself?. If you need the store in a React Component, there are better options, like using React-Redux and its connect function (more on that below!).

Mark Erikson, one of the Redux maintainers, added a few thoughts on Reddit which I wanted to share here. He says:

In general, don’t import the store just to use it in a component.

For other logic, if possible try to put it in a thunk or other middleware so that it doesn’t have to reference the store directly.

But, yes, that’s not always possible. One of the apps I work on is still about 70% Backbone, and we’ve now got some files that need to work with both data from Backbone models and the Redux store. In those files, we do indeed literally do import {store} from "store" and reference it directly, because we don’t have any other option.

So, that’s not ideal, but if you gotta do it, do it.

Dispatch Actions Outside a React Component

If you need to dispatch actions from outside a React component, the same technique will work: import the store, then call store.dispatch(), passing the action you need to dispatch. It works the same as the dispatch function you get from props via react-redux’s connect function.

Option 2: Access Redux State from a Thunk

If you need access to the Redux store’s state from inside a thunk action creator, that’s even easier. You don’t even need to export the store, because thunk actions receive a getState argument.

Here’s an example action creator that pulls a JWT token out of the state before making an API call.

actions.js
export function getProtectedThing() {
  return (dispatch, getState) => {
    // grab current state
    const state = getState();

    // get the JWT token out of it
    // (obviously depends on how your store is structured)
    const authToken = state.currentUser.token;

    // Pass the token to the server
    return fetch('/user/thing', {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${authToken}`
      }
    }).then(res => res.json());
  }
}

If you don’t want to put fetch calls directly in your thunk actions (I don’t blame you), you can move them to another file like api.js but you’ll need to add an extra argument to all of them so that you can pass in the token.

Option 3: Use Middleware and Intercept an Action

If you don’t like (or can’t use) either of the above solutions, maybe this one will work.

Write a custom middleware that intercepts a particular action. Then, you can pull some data out of the action (before it hits the store) or the store itself and update a variable somewhere else.

Here’s how that might work with an API that requires a JWT token. Let’s pretend your app dispatches a LOGIN_SUCCESS action with a JWT token after the user logs in successfully.

index.js
const saveAuthToken = store => next => action => {
  if(action.type === 'LOGIN_SUCCESS') {
    // after a successful login, update the token in the API
    api.setToken(action.payload.authToken);
  }

  // continue processing this action
  return next(action);
}

const store = createStore(
  reducer,
  applyMiddleware(saveAuthToken)
);

Then in your api file, you can have a setToken function that updates a local variable with the token.

api.js
let currentAuthToken = null;

export function setToken(token) {
  currentAuthToken = token;
}

export function getProtectedThing() {
  // Pass the token to the server
  return fetch('/user/thing', {
    method: 'GET',
    headers: {
      Authorization: `Bearer ${currentAuthToken}`
    }
  }).then(res => res.json());
}

If you’re using the axios HTTP library, then you can set the Authorization header on the axios instance itself, and axios will automatically include it in any further HTTP request.

import axios from 'axios';

export function setToken(token) {
  axios.defaults.headers.common['Authorization'] =
      `Bearer ${token}`;
}

Option 4: Pass the Value From a React Component

It’s simple to get access to the store inside a React component – no need to pass the store as a prop or import it, just use the connect function from React Redux, and supply a mapStateToProps function that pulls out the data you need.

Then, inside the component, you can pass that data to a function that needs it.

import React from 'react';
import { connect } from 'react-redux';
import * as api from 'api';

const ItemList = ({ authToken, items }) => {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          {item.name}
          <button
            onClick={
              () => api.deleteItem(item, authToken)
            }>
            DELETE THIS ITEM
          </button>
        </li>
      )}
    </ul>
  )
}

const mapStateToProps = state => ({
  authToken: state.currentUser && state.currentUser.authToken,
  items: state.items
});

export connect(mapStateToProps)(ItemList);

Which Option Is Best?

The best one depends on your needs.

I like Option 1 (export the store) for its simplicity, and I’ve used it in the past – but only on an app that didn’t need server rendering.

At some point, our import became troublesome because of a circular dependency, and we switched to something closer to Option 3 (intercepting an action).

Option 2 (getState in a thunk) is fine if it makes sense to access your data inside a thunk. Sometimes that’s not where you need it, though, and it might feel strange to mix a Redux concern with your generic utility code.

Personally, I’m not crazy about Option 4 (passing it from a React component), because I don’t like having to thread an auth token through components. That feels like it should be purely an API concern. But it gets the job done, and sometimes the most expedient option wins.

At the end of the day, “done” is better than “perfect.” Pick one and write it up ;)

Learning React can be a struggle -- so many libraries and tools!
My advice? Ignore all of them :)
For a step-by-step approach, read my book Pure React.

Best thing that ever happened to my career 👏🏽

I’m very happy I bought this. Didn’t fully grasp the fundamentals from Udemy courses I’ve been taking. I’m buying the Redux course immediately after I’m done with this. THANK YOU SOO MUCH

– Oluwafemi
comments powered by Disqus