1

I'm trying to fetch the token from my auth reducer in my app for making subsequent requests for more resources.

The problem is I can't access the store.getState() or store anywhere outside my components. Like actions/api service.

I remember earlier making an app where I was able to without any problems.

Here's a contrived example : https://stackblitz.com/edit/react-redux-app-1wxxab?file=index.js I've made a basic todo app and you can see in actions/index.js that when I console.log(store) I get undefined.

Update: I've updated the example to emphasise the problem, where I can't access it in a separate file api.js

Another Example: https://stackoverflow.com/a/43944684/1356046 they say it works like this but I'm not able to reproduce it.

Anyway to fix this and access the store state? Have tried everything since yesterday. Thanks.

SinSync
  • 488
  • 1
  • 6
  • 16
  • I guess before you were using [redux thunk actions](https://github.com/reduxjs/redux-thunk) you got getState and dispatch there. – HMR Oct 30 '19 at 13:19
  • I'm using `redux-starter-kit` which has redux-thunk as the default middleware(so it's included) : S – SinSync Oct 30 '19 at 13:56
  • Then both dispatch and getState is available in your action: `const someAction = (id)=>(dispatch,getState)=>{if(seletItem(getState(id))){return};dispatch(loading(id);...}` – HMR Oct 30 '19 at 14:05
  • The thing is I'm trying to use it in an API service so when someone sign's in then I store the token and I want my `api` request to include that token. – SinSync Oct 30 '19 at 14:17
  • @HMR I've updated the example if that helps – SinSync Oct 30 '19 at 14:30
  • 2
    You could either pass the token from your action to the API function (in action you have getState and you can get the token from store). Or when you sign in save the token to localStorage, this way when the user has logged in and opens other tabs, leaves and returns to your site the user is not asked to log in again if the token wasn't expired. – HMR Oct 30 '19 at 15:09
  • If I start storing things in localStorage it will soon start to mimic my entire User object which will be problematic. Getting the store state is imperative to the application for a lot of UI decision I want to take. https://stackoverflow.com/a/43944684/1356046 -> this is what I'm doing in my example but without success. Thanks for taking the time, really appreciate it :) – SinSync Oct 30 '19 at 16:11
  • Could you post the code in the question? – Jacob Oct 30 '19 at 19:49

3 Answers3

1

Export store when you created it, then use it eg store.dispatch(action); or create api specific middleware

See What is the best way to access redux store outside a react component? for many examples

Update:

https://stackblitz.com/edit/react-redux-app-1mswrv

store.js:

import { configureStore } from "redux-starter-kit";
import rootReducer from './reducers'

export const store = configureStore({
  reducer: rootReducer,
});

index.js:

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './components/App'
import { store } from "./store";

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

api.js:

import {store} from './store';

// Some promise which dispatches a fetch request after getting the token from the store

export const getSomething = () => {
  console.log('store:', store);
  return store;
}
givehug
  • 1,863
  • 1
  • 14
  • 19
  • The problem is I cannot access the `store` instance inside the `api.js`(which is what happens in the link) if I was able access exported `store` that would solve the problem. If you look at my example, I am exporting the store. :) – SinSync Oct 30 '19 at 14:54
  • Aha, I see, updated. You had your imports/exports a little messed up. Better move store initialisation to separate module to eliminate dependency conflicts, see example, store is logged to console. – givehug Oct 30 '19 at 18:40
1

Cloned your stackblitz and made the following changes:

In your api file do the following:

import store from './store';

Add a store.js with the content:

import { configureStore } from "redux-starter-kit";
import rootReducer from './reducers'


const store = configureStore({
  reducer: rootReducer,
});

export default store;

And changed your index.js to:

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './components/App'
import store from './store'


render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

I would still opt for saving a token in local storage, if the user is logged in and opens another tab the user needs to log in again because the other tab has no access to the token, same when the user goes to another site and then back to your site.

HMR
  • 37,593
  • 24
  • 91
  • 160
0

If you are using redux-thunk and passing your api with withExtraArgument() to the thunk middleware passed to createStore() then you can use a lazy callback function to inject selectors for your API token (or whatever else you'd like) to a wrapped API helper function.

Personally, I prefer this approach as it decouples the api helper from redux. The API helper does not have to know about redux at all for this to work. Insread of imports, you simply inject the selector into the API helper, which then calls them when needed (long after the store is initialized).

api.js

// function accepting callback that returns a pretty 
// standard post function
export const post = (getHeaders) => (url, body) => {
  return fetch(url, { 
    method: 'POST',
    headers: getHeaders(),
    ...
  }
}

store.js

import * as api from './utils/api';
import { selectAuthHeaders } from './features/auth'

const store = createStore(
  rootReducer,
  initialState,
  applyMiddleware(
    thunk.withExtraArgument({
      post: api.post(() => {
        return selectAuthHeaders(store.getState());
      }
    })
  )
)