How to use react-redux with next js and class components

Viewed 716

I'm trying to use redux with NextJS and class components but can't figure out how to get rid of this error:

Error: Could not find "store" in the context of "Connect(CounterDisplay)". Either wrap the root component in a <Provider>, or pass a custom React context provider to and the corresponding React context consumer to Connect(CounterDisplay) in connect options.

// pages/_app.js

import '../styles/globals.css';
import 'bootstrap/dist/css/bootstrap.css';
import {Provider} from 'react-redux';
import {useStore} from '../core/redux/store';

export default function App({Component, pageProps}) {
  const store = useStore(pageProps.initialReduxState);

  return (
      <Provider store={store}>
        <Component {...pageProps} />
      </Provider>
  );
}
// pages/index.js

import React from 'react';
import Page from '../components/page'
import CounterDisplay from '../components/CounterDisplay';

export default function Index() {
  // return <Page />
  return (
      <React.Fragment>
        <CounterDisplay/>
      </React.Fragment>
  )
}
// components/CounterDisplay.js

import React from 'react';
import connect from 'react-redux/lib/connect/connect';

class CounterDisplay extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
        <div>
          <span>{this.props.count}</span>
        </div>
    );
  }
}


function mapStateToProps(state, ownProps) {
  return {
    count: state.count
  }
}

export default connect(mapStateToProps)(CounterDisplay);

Weird thing is that I already wrapped the root component in a <Provider> so it should work fine according to the error message.

EDIT:

// core/redux/store

import {useMemo} from 'react';
import {createStore, applyMiddleware} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';

let store;

const initialState = {
  lastUpdate: 0,
  light: false,
  count: 0,
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'TICK':
      return {
        ...state,
        lastUpdate: action.lastUpdate,
        light: !!action.light,
      };
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1,
      };
    case 'DECREMENT':
      return {
        ...state,
        count: state.count - 1,
      };
    case 'RESET':
      return {
        ...state,
        count: initialState.count,
      };
    default:
      return state;
  }
};

function initStore(preloadedState = initialState) {
  return createStore(
      reducer,
      preloadedState,
      composeWithDevTools(applyMiddleware()),
  );
}

export const initializeStore = (preloadedState) => {
  let _store = store ?? initStore(preloadedState);

  // After navigating to a page with an initial Redux state, merge that state
  // with the current state in the store, and create a new store
  if (preloadedState && store) {
    _store = initStore({
      ...store.getState(),
      ...preloadedState,
    });
    // Reset the current store
    store = undefined;
  }

  // For SSG and SSR always create a new store
  if (typeof window === 'undefined') return _store;
  // Create the store once in the client
  if (!store) store = _store;

  return _store;
};

export function useStore(initialState) {
  const store = useMemo(() => initializeStore(initialState), [initialState]);
  return store;
}
1 Answers

In your CounterDisplay component you need to change import of connect :

import connect from 'react-redux/lib/connect/connect';

line to:

import {connect} from 'react-redux';

react-redux most probably can not see the same store if you do not import it from the addon's root folder.

Related