318

@connect works great when I'm trying to access the store within a react component. But how should I access it in some other bit of code. For eg: let's say I want to use an authorization token for creating my axios instance that can be used globally in my app, what would be the best way to achieve that?

This is my api.js

// tooling modules
import axios from 'axios'

// configuration
const api = axios.create()
api.defaults.baseURL = 'http://localhost:5001/api/v1'
api.defaults.headers.common['Authorization'] = 'AUTH_TOKEN' // need the token here
api.defaults.headers.post['Content-Type'] = 'application/json'

export default api

Now I want to access a data point from my store, here is what that would look like if I was trying to fetch it within a react component using @connect

// connect to store
@connect((store) => {
  return {
    auth: store.auth
  }
})
export default class App extends Component {
  componentWillMount() {
    // this is how I would get it in my react component
    console.log(this.props.auth.tokens.authorization_token) 
  }
  render() {...}
}

Any insights or workflow patterns out there?

Subodh Pareek
  • 3,907
  • 2
  • 11
  • 9
  • You don't want you're Axios instance to live in a redux middleware ? It will be available by all your application this way – Emrys Myrooin Jul 19 '16 at 14:21
  • 2
    You can import the `api` in `App` class and after getting the authorization token you can do `api.defaults.headers.common['Authorization'] = this.props.auth.tokens.authorization_token;`, And at the same time you can store it in localStorage as well, so when the user refreshes the page, you can check if the token exists in localStorage and if it does, you can set it., I think it will be best to set the token on api module as soon as you get it. – Dhruv Kumar Jha Sep 05 '16 at 08:55
  • 1
    Dan Abromov provides an example in the issue queue here: https://github.com/reactjs/redux/issues/916 – chrisjlee Mar 09 '17 at 05:31
  • 1
    If you just need to access a particular state from a particular reducer, you can try with [redux-named-reducers](https://github.com/mileschristian/redux-named-reducers) it allows you access the latest state from anywhere. – miles_christian Apr 07 '18 at 08:16
  • Most of the answers are wrong, you should never import store file directly. For correct answer checkout my answer below. That approach is suggested by react itself. – Tapan Sharma Jun 27 '23 at 08:43

12 Answers12

213

Export the store from the module you called createStore with. Then you are assured it will both be created and will not pollute the global window space.

MyStore.js

const store = createStore(myReducer);
export store;

or

const store = createStore(myReducer);
export default store;

MyClient.js

import {store} from './MyStore'
store.dispatch(...)

or if you used default

import store from './MyStore'
store.dispatch(...)

For Multiple Store Use Cases

If you need multiple instances of a store, export a factory function. I would recommend making it async (returning a promise).

async function getUserStore (userId) {
   // check if user store exists and return or create it.
}
export getUserStore

On the client (in an async block)

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
  • 72
    WARNING for isomorphic apps: doing this server-side will share the redux store across all your users!!! – Lawrence Wagerfield Dec 05 '17 at 10:00
  • The question does not ask how to manage multiple stores for multiple users or how to manage server-side state; It simple questions how to access the store that was defined in the specified code. – Steven Spungin Dec 05 '17 at 13:25
  • 6
    The question also doesn't explicitly state "browser" either. Since both Redux and JavaScript can be used on either server or browser, it's much safer to be specific. – Lawrence Wagerfield Dec 06 '17 at 10:29
  • 10
    exporting store seems to create a nightmare of cyclic imports, createStore includes your reducers, which require your actions (at least the action type enum, and Action interfaces), which must not import anything that tries to import store. So you must not use store in either your reducers or actions (or wrangle some other way around the cyclic import.) – Eloff Jul 29 '18 at 19:59
  • 13
    This is the correct answer, but if you (like me) want to read instead of dispatch an action in the store, you need to call `store.getState()` – Juan José Ramírez Sep 05 '18 at 21:55
  • @JuanJoséRamírez that is true, but OP posted question about obtaining store, not calling functions on it, so the dispatch call in the example was just icing on the cake. Using the store is a different issue. – Steven Spungin Sep 05 '18 at 22:00
  • 4
    Well, my interpretation on "access redux store" was "read" the store. Just trying to help others. – Juan José Ramírez Sep 06 '18 at 22:29
  • Making it async is not correct and won't give it any real value since all the redux store operations are synchronous. – Islam Attrash Mar 03 '19 at 17:47
  • This would be for the creation factory, not for the insance methods. – Steven Spungin Mar 03 '19 at 18:35
  • A perfect example of an async factory would be a per-user store that needed to fetch the initial state from a database before it could be used. Don''t confuse the store creation with the store instance methods. – Steven Spungin Mar 04 '19 at 23:55
  • I have multiple iframes that need access to the state of the other iframes. I know it's kinda hacky but I think having the store on the window would be better than using messages from iframe to iframe to trigger a state change and render. Any thoughts?? – Sean Malter Jul 24 '19 at 21:27
  • @SeanMalter If you don't put the exported store on the window, you will have to do messaging to your iframe, and that would not work so well with reactivity. So not hacky at all. – Steven Spungin Jul 24 '19 at 21:59
  • is this better than connecting the store to the component, in which cases we should follow this what are the pros and cons of connecting to store from outside of the component? – Siluveru Kiran Kumar Aug 28 '19 at 18:17
  • @siluverukirankumar Not really sure what you mean by 'is this better than connecting the store to the component'. The OP needed a way to access the store outside a component. If you are inside a component, the store is already available via `this.$store`. That is much better, as you don't need the import. – Steven Spungin Aug 28 '19 at 19:19
  • @StevenSpungin what is the OP in your above comment. and also store is available in props right when we connect the store to the component through matpStateToProps. but this.$store how it could be? correct if I'm wrong. – Siluveru Kiran Kumar Aug 29 '19 at 07:21
  • OP = original poster, or the person that started this thread by asking a specific question. There are often times we need to access the store outside a vue component, such as a logger, sync engine, communication library etc. and the state has not been fed to us. For your other question, in a template you can access store as `$store`, in script you access it as `this.$store`. mapStateToProps is just sugar, and is calling this.$store behind the scenes. Eventually you will need to get the store outside of the mapXXX functions. We are getting a bit off-topic though... – Steven Spungin Aug 29 '19 at 07:31
  • It's not give updated redux data. It's return initial redux. – Dhaduk Mitesh Jul 02 '21 at 12:45
  • Not working for me. Posted a question in https://stackoverflow.com/questions/71453029/failing-to-import-an-exported-redux-store-in-a-react-native-ts-module – Yossi Mar 12 '22 at 21:37
  • @yossi You are trying to access the store from 'within the component', not 'outside of it'. That is a very different usage. – Steven Spungin Mar 13 '22 at 00:16
73

Found a solution. So I import the store in my api util and subscribe to it there. And in that listener function I set the axios' global defaults with my newly fetched token.

This is what my new api.js looks like:

// tooling modules
import axios from 'axios'

// store
import store from '../store'
store.subscribe(listener)

function select(state) {
  return state.auth.tokens.authentication_token
}

function listener() {
  let token = select(store.getState())
  axios.defaults.headers.common['Authorization'] = token;
}

// configuration
const api = axios.create({
  baseURL: 'http://localhost:5001/api/v1',
  headers: {
    'Content-Type': 'application/json',
  }
})

export default api

Maybe it can be further improved, cause currently it seems a bit inelegant. What I could do later is add a middleware to my store and set the token then and there.

Subodh Pareek
  • 3,907
  • 2
  • 11
  • 9
38

You can use store object that is returned from createStore function (which should be already used in your code in app initialization). Than you can use this object to get current state with store.getState() method or store.subscribe(listener) to subscribe to store updates.

You can even save this object to window property to access it from any part of application if you really want it (window.store = store)

More info can be found in the Redux documentation .

tschoffelen
  • 518
  • 7
  • 21
trashgenerator
  • 691
  • 4
  • 7
  • 29
    sounds kinda hacky to save `store` to the `window` – Vic Oct 13 '16 at 17:04
  • 5
    @Vic Sure it is :) And normally you don't want to do it. I just wanted to mention that you can do whatever you want with this variable. Probably the the best idea would be to store it in file where you created your "createStore" lives and then just import from it. – trashgenerator Oct 18 '16 at 13:14
  • 1
    I have multiple iframes that need access to the state of the other iframes. I know it's kinda hacky but I think having the store on the window would be better than using messages to iframes. Any thoughts?? @Vic @trashgenerator? – Sean Malter Jul 24 '19 at 21:25
17

You can use Middleware according to How can I access the store in non react components?:

Middleware

function myServiceMiddleware(myService) {
  return ({ dispatch, getState }) => next => action => {
    if (action.type == 'SOMETHING_SPECIAL') {
      myService.doSomething(getState());
      myService.doSomethingElse().then(result => dispatch({ type: 'SOMETHING_ELSE', result }))
    }
    return next(action);
  }
}

Usage

import { createStore, applyMiddleware } from 'redux'
const serviceMiddleware = myServiceMiddleware(myService)
const store = createStore(reducer, applyMiddleware(serviceMiddleware))

Further Reading: Redux Docs > Middleware

KyleMit
  • 30,350
  • 66
  • 462
  • 664
sanchit
  • 2,328
  • 2
  • 18
  • 22
13

Like @sanchit proposed middleware is a nice solution if you are already defining your axios instance globally.

You can create a middleware like:

function createAxiosAuthMiddleware() {
  return ({ getState }) => next => (action) => {
    const { token } = getState().authentication;
    global.axios.defaults.headers.common.Authorization = token ? `Bearer ${token}` : null;

    return next(action);
  };
}

const axiosAuth = createAxiosAuthMiddleware();

export default axiosAuth;

And use it like this:

import { createStore, applyMiddleware } from 'redux';
const store = createStore(reducer, applyMiddleware(axiosAuth))

It will set the token on every action but you could only listen for actions that change the token for example.

erksch
  • 501
  • 4
  • 14
11

It might be a bit late but i think the best way is to use axios.interceptors as below. Import urls might change based on your project setup.

index.js

import axios from 'axios';
import setupAxios from './redux/setupAxios';
import store from './redux/store';

// some other codes

setupAxios(axios, store);

setupAxios.js

export default function setupAxios(axios, store) {
    axios.interceptors.request.use(
        (config) => {
            const {
                auth: { tokens: { authorization_token } },
            } = store.getState();

            if (authorization_token) {
                config.headers.Authorization = `Bearer ${authorization_token}`;
            }

            return config;
        },
       (err) => Promise.reject(err)
    );
}
Mert Simsek
  • 700
  • 7
  • 12
8

Doing it with hooks. I ran into a similar problem, but I was using react-redux with hooks. I did not want to lard up my interface code (i.e., react components) with lots of code dedicated to retrieving/sending information from/to the store. Rather, I wanted functions with generic names to retrieve and update the data. My path was to put the app's

const store = createSore(
   allReducers,
   window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
 );

into a module named store.js and adding export before const and adding the usual react-redux imports in the store.js. file. Then, I imported to index.js at the app level, which I then imported into index.js with the usual import {store} from "./store.js" The child components then accessed the store using the useSelector() and useDispatch() hooks.

To access the store in non-component front end code, I used the analogous import (i.e., import {store} from "../../store.js") and then used store.getState() and store.dispatch({*action goes here*}) to handled retrieving and updating (er, sending actions to) the store.

Charles Goodwin
  • 584
  • 5
  • 8
6

For TypeScript 2.0 it would look like this:

MyStore.ts

export namespace Store {

    export type Login = { isLoggedIn: boolean }

    export type All = {
        login: Login
    }
}

import { reducers } from '../Reducers'
import * as Redux from 'redux'

const reduxStore: Redux.Store<Store.All> = Redux.createStore(reducers)

export default reduxStore;

MyClient.tsx

import reduxStore from "../Store";
{reduxStore.dispatch(...)}
Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • 7
    If you are voting down, please add a comment why. – Ogglas Dec 21 '17 at 07:39
  • 3
    Down voted because your answer lacks explanation and uses TypeScript rather than Javascript. – Will Feb 28 '18 at 21:06
  • 3
    @Will Thank you for saying why. Imao the code does not require specification but if you would like something specific explained please say what. TypeScript is indeed used but if the typings are removed the same code will run in ES6 without a problem. – Ogglas Feb 28 '18 at 21:19
  • Keep in mind this will be very bad if you are doing sever-side-rendering, it will share state with all requests. – Rob Evans May 17 '19 at 18:51
  • I'm using `useDispatch` in all my code so your solution of calling dispatch directly on the reduxStore is exactly what I needed, Thank you! – Ralph Ritoch Feb 03 '23 at 05:04
5

export my store variable

export const store = createStore(rootReducer, applyMiddleware(ReduxThunk));

in action file or your file need them import this (store)

import {store} from "./path...";

this step get sate from store variable with function

const state = store.getState();

and get all of state your app

sajjad seafi
  • 59
  • 1
  • 2
0

An easy way to have access to the token, is to put the token in the LocalStorage or the AsyncStorage with React Native.

Below an example with a React Native project

authReducer.js

import { AsyncStorage } from 'react-native';
...
const auth = (state = initialState, action) => {
  switch (action.type) {
    case SUCCESS_LOGIN:
      AsyncStorage.setItem('token', action.payload.token);
      return {
        ...state,
        ...action.payload,
      };
    case REQUEST_LOGOUT:
      AsyncStorage.removeItem('token');
      return {};
    default:
      return state;
  }
};
...

and api.js

import axios from 'axios';
import { AsyncStorage } from 'react-native';

const defaultHeaders = {
  'Content-Type': 'application/json',
};

const config = {
  ...
};

const request = axios.create(config);

const protectedRequest = options => {
  return AsyncStorage.getItem('token').then(token => {
    if (token) {
      return request({
        headers: {
          ...defaultHeaders,
          Authorization: `Bearer ${token}`,
        },
        ...options,
      });
    }
    return new Error('NO_TOKEN_SET');
  });
};

export { request, protectedRequest };

For web you can use Window.localStorage instead of AsyncStorage

  • I don´t think this is useful because you are accessing localstorage directly, without using redux store – LeoPucciBr Aug 03 '21 at 13:11
  • LocalStorage can be accessed by javascript which leaves your application vulnerable. Storing it in state with an axios interceptor to refresh the token on a 401, or in a httpOnly cookie is generally recommended. – Predator Aug 21 '22 at 09:15
0

I faced a similar problem - I wanted to set up a global Axios configuration that could be accessed from anywhere within React (component, outside of component, inside of thunk action..)

I ended up writing a thunk that returns the config object. The benefit is that thunks have access to the store thanks to getState() so you don't have to reinvent the wheel. Maybe this solution helps someone ;)

1. THUNK

export const getAxiosConfig = (payload) => {
      return (dispatch, getState) => {
      
      const { app } = getState();
    
      const axiosConfig: AxiosRequestConfig = {
        baseURL: `${process.env.BACKEND_API}`,
        headers: {
          Authorization: `Bearer ${app.token}` 
        }
      };

      return axiosConfig;
    }
 }

2. GET THE CONFIG

const axiosConfig = dispatch(getAxiosConfig(null));

3. MAKE API CALL WITH CONFIG

const { data } = await axios.get(/resource/${resourceId}, axiosConfig );

kv2016
  • 3
  • 3
0

Importing store directly is not the correct way!!!

Here are the possible ways using which you can access redux store outside a react component

  1. Pass along references to dispatch from components as arguments to the relevant functions
  2. Write the logic as middleware and add them to the store at setup time
  3. Inject the store instance into the relevant files as the app is being created.

My favorite is the third option of injecting the store when the app is being created:

One common use case is reading API authorization information such as a token from the Redux state, inside of an Axios interceptor. The interceptor file needs to reference store.getState(), but also needs to be imported into API layer files, and this leads to circular imports.

You can expose an injectStore function from the interceptor file instead:

common/api.js

let store

export const injectStore = _store => {
  store = _store
}

axiosInstance.interceptors.request.use(config => {
  config.headers.authorization = store.getState().auth.token
  return config
})

Then, in your entry point file, inject the store into the API setup file:

index.js

import store from './app/store'
import { injectStore } from './common/api'
injectStore(store)

This way, the application setup is the only code that has to import the store, and the file dependency graph avoids circular dependencies.

Above ways are suggested by react redux itself. You can check here!