Access Redux store outside React when using next-redux-wrapper

Viewed 3260

I have a NextJS React app that uses the next-react-wrapper (basically a HOC) on _app.tsx like so:

_app.tsx

...
import withRedux from 'next-redux-wrapper';

class Page extends App<Props> {
  ...
}

export default withRedux(reduxStore)(Page);

store.ts

import { applyMiddleware, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';

import rootReducer from './reducer';

export default (
  initialState: any = undefined,
) => createStore(
  rootReducer,
  initialState,
  composeWithDevTools(applyMiddleware()),
);

I'm struggling to work out how to access the store outside of React such as in a simple helper function. My store.ts file exports a makeStore function which is needed (including the initial state) for the next-redux-wrapper HOC.

I could access the store in a React component and pass it to my helper functions as an argument each time but that seems messy.

Is there a way to access the store direct from non React helper function modules?

6 Answers

It may not be preferable, but the store can be accessed from the window using a storeKey. The default key is __NEXT_REDUX_STORE__ and using it look like this:

window.__NEXT_REDUX_STORE__.getState()

Here's where that happens

The key (storeKey) can be changed in the second options parameter passed to the withRedux function parameter. For your implementation it looks like this:

export default (
  initialState: any = undefined,
  { storeKey: 'whateveryouwant' } // the name on the exposed store variable
) => createStore(
  rootReducer,
  initialState,
  composeWithDevTools(applyMiddleware()),
);

I had the same issue but found the solution.

The Readme of this lib gives the example of makeStore function like this:

const makeStore = (initialState, options) => {
    return createStore(reducer, initialState);
};

You need to modify it a bit

let store;
const makeStore = (initialState, options) => {
    store = createStore(reducer, initialState);
return store;
};

export {store}

Now you can import the store from wherever you want.

Worked for me with TS

let store: ReturnType<typeof configStore>

const configStore = () => configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunkMiddleware),
  devTools: process.env.NODE_ENV !== 'production',
});

export const makeStore = () => {
  store = configStore();
  return store;
};

export type AppStore = ReturnType<typeof makeStore>;
export type AppState = ReturnType<typeof combinedReducer>;
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, AppState, unknown, Action>;
export type AppDispatch = ReturnType<AppStore['dispatch']>;

export const wrapper = createWrapper<AppStore>(makeStore, { debug: false });

export { store };

You can create high-order function to wrap any other function with store. Here is simple example that pass store as this argument to any other function.

function withReduxFunction(store) {
    return function (connectedFunction) {
        return function (...args) {
            connectedFunction.call(store, ...args);
        }
    }
}

And usage. For example we want to provide store to this function

function doSomthingWothStore(arg1, arg2) {
    console.log(this);   // This will be store
    console.log("arg1: " + arg1 + " arg2 " + arg2);
}

Do

const funcWithStore = withReduxFunction(store)(doSomthingWothStore);

Now you can call funcWithStore and this will be equal to store.

You can play with high-order function to make it suitable for you (i.e. pass store as first argument and so on).

Also you can take a look at useDispatch and useSelector hooks from react-redux. They also should work with any function.

You can import store module wherever it is needed and directly access the store functions like store.getState(). However, you need to subscribe to store to be notified of any change in the state.

You can create store first and then return it from makeStore()

export const store = createStore(...)
const makeStore() {
  return store
}
export const wrapper = createWrapper(makeStore, { debug: true })
Related