tshaddix/webext-redux

How to fix Uncaught TypeError: Cannot read properties of undefined (reading 'state')

alex-robert888 opened this issue ยท 7 comments

Hello,

I am trying to use the webext-redux package and I came across an error which I have a hard time solving.

Basically, I followed the first 2 required steps from the README of the webext-redux repository.

  1. I have a service worker file, background.js, where I use the wrapStore function to wrap my Redux store. The background script seems to be executing fine, with no errors:

background.js

import store from '../src/store/store.js';
import {wrapStore} from 'webext-redux';

console.log("BEFORE wrap.")
wrapStore(store);
console.log("AFTER wrap")
  1. I created a proxy store in the React app and passed it to the Provider:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import {Provider} from 'react-redux';
import {Store} from 'webext-redux';

const root = ReactDOM.createRoot(document.getElementById('root'));
const proxyStore = new Store();

proxyStore.ready().then(() => {
  root.render(
    <React.StrictMode>
      <Provider store={proxyStore}>
        <App />
      </Provider>
    </React.StrictMode>
  );
});

The problem occurs when I try to use a Redux selector in one of my components, to retrieve the value from the store. The store.js looks like this:

import { configureStore } from '@reduxjs/toolkit';
import reliabilityAnalysisSliceReducer from './reliabilityAnalysisSlice.js';

export default configureStore({
  reducer: {
    reliabilityAnalysis: reliabilityAnalysisSliceReducer
  }
})

reliabilityAnalysisSlice.js

import axios from 'axios';
import { createSlice } from '@reduxjs/toolkit';

// Slice
export const reliabilityAnalysisSlice = createSlice({
  name: "reliabilityAnalysis",
  initialState: {
    shouldShowReliabilityAnalysis: false
  },
  reducers: {
    showReliabilityAnalysis: (state, _) => {
      state.shouldShowReliabilityAnalysis = true;
    },

    hideReliabilityAnalysis: (state, _) => {
      state.shouldShowReliabilityAnalysis = false;
    }
  }
})

// Actions
export const { 
  showReliabilityAnalysis, 
  hideReliabilityAnalysis 
} = reliabilityAnalysisSlice.actions


// Reducer
export default reliabilityAnalysisSlice.reducer;

And I use the selector like this:
const shouldShowReliabilityAnalysis = useSelector(state => state.shouldShowReliabilityAnalysis);

Which throws the following error:
image
image
image

There are hardly any sources on the internet that could help me, so I would higlhy appreciate if someone could lend me a hand.

Is this this package still supported and funcitonal? Or am I doing something wrong? Any hints on how to debug?

What version of react-redux do you use? I'm getting this error on v8 and it does work on v7. Reading changelogs of react-redux I found that it uses new useSyncExternalStore from react 18. This package hasn't been updated for a while so it may not support this new functionality. In my case it complains about it: at useSyncExternalStore......
image
For now I would stay at v7 but still I hope it will be addressed and fixed

Debugging it I discovered that "this" is undefined only sometimes.
image
image
Edit: all calls with this stack trace are undefined:
image
and this is the good working stack
image

There's two things happening here:

  • webext-redux has its Store type written as a class, not as a closure the way the real Redux library does. So, store.getState in webext-redux is actually a class method that relies on this
  • React-Redux v8 passes store.getState as an argument to useSyncExternalStore, which will be called later as just getState(). That means that there's no this binding applied to the getState call.

This works fine with normal Redux, but because webext-redux uses a class instance that explodes.

Tbh it's almost an accident that this ever worked with React-Redux until now.

So the only solution for this is to drop redux to v7?

@plusminushalf : React-Redux, yeah. Also looks like there's an open PR here in this repo (#289), but it hasn't been merged.

@plusminushalf if you want to use V8 you can use a workaround to make it work.

One possible solution is described here reduxjs/react-redux#1963 . Creating the store like this might fix the problem.

const store = new Store()
Object.assign(store, {
  dispatch: store.dispatch.bind(store),
  getState: store.getState.bind(store),
  subscribe: store.subscribe.bind(store),
})

Another solution would be to use a modified version of webext-redux with the applied fix. This is what I did. (You can find my rough notes in how to do that over here: https://dermeck.github.io/use-modified-npm-package/ )

btw I was having issues with thunk not working in redux toolkit, and this seems to have resolved it.

export const createStoreProxy = (portName: string) => {
  const store = new Store({ portName });
  const { dispatch: originalDispatch } = store;

  // Fix for unresolved bug in webext-redux: https://github.com/tshaddix/webext-redux/issues/286
  Object.assign(store, {
    dispatch: ((action: AnyAction) => {
      if (typeof action === 'function') {
        // @ts-ignore
        return action(store.dispatch, store.getState);
      }

      return originalDispatch(action);
    }),
    getState: store.getState.bind(store),
    subscribe: store.subscribe.bind(store)
  });

  return store;
};