React + Redux + Typescript Chrome Extension Popup Script - Redux Devtools Shows "No Store Found"

1.2k Views Asked by At

I am creating a chrome extension that is mainly served in a popup script.

Manifest.json

// public/manifest.json

{
  "manifest_version": 3,
  "version": "3.0.0",
  "name": "__MSG_appName__",
  "description": "__MSG_appDesc__",
  "default_locale": "en",
  "author": "lbragile",
  "homepage_url": "some_url_not_relevant_to_question_at_hand",
  "permissions": ["tabs", "storage"],
  "optional_permissions": ["contextMenus", "alarms", "downloads", "downloads.shelf"],
  "icons": {
    "16": "images/logo16.png",
    "48": "images/logo48.png",
    "128": "images/logo128.png"
  },
  "action": {
    "default_icon": {
      "16": "images/logo16.png",
      "48": "images/logo48.png",
      "128": "images/logo128.png"
    },
    "default_popup": "index.html",
    "default_title": "title"
  },
  "background": {
    "service_worker": "background.js"
  },
  "incognito": "split"
}

Package.json

// package.json

{
  "name": "name",
  "version": "0.0.1",
  "description": "description",
  "author": "lbragile",
  "private": true,
  "dependencies": {
    "nanoid": "^3.1.30",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.5",
    "react-router-dom": "^5.3.0",
    "react-scripts": "4.0.3",
    "redux": "^4.1.1"
  },
  "devDependencies": {
    "@babel/core": "^7.13.14",
    "@babel/preset-env": "^7.13.12",
    "@babel/preset-react": "^7.13.13",
    "@fortawesome/fontawesome-svg-core": "^1.2.36",
    "@fortawesome/free-regular-svg-icons": "^5.15.4",
    "@fortawesome/free-solid-svg-icons": "^5.15.4",
    "@fortawesome/react-fontawesome": "^0.1.15",
    "@types/chrome": "0.0.159",
    "@types/express": "^4.17.13",
    "@types/node": "^16.10.3",
    "@types/react": "^17.0.15",
    "@types/react-dom": "^17.0.9",
    "@types/react-redux": "^7.1.19",
    "@types/react-router-dom": "^5.1.8",
    "@types/redux-immutable-state-invariant": "^2.1.2",
    "@types/remote-redux-devtools": "^0.5.5",
    "@types/styled-components": "^5.1.12",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "babel-loader": "^8.2.3",
    "babel-plugin-styled-components": "^1.13.2",
    "copy-webpack-plugin": "^9.0.1",
    "eslint": "^7.11.0",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-plugin-styled-components-a11y": "0.0.34",
    "redux-devtools-extension": "^2.13.9",
    "styled-components": "^5.3.0",
    "stylelint": "^13.13.1",
    "stylelint-config-standard": "^22.0.0",
    "ts-loader": "^9.2.6",
    "typescript": "^4.3.5",
    "url-loader": "^4.1.1",
    "webpack": "^5.59.1",
    "webpack-cli": "^4.9.1"
  },
  "scripts": {
    "lint": "npx eslint {src,public}/**/**/*.[jt]s -c config/.eslintrc.js --ignore-path .gitignore .",
    "lint:style": "npx stylelint {src,public}/**/**/*.css --config config/.stylelintrc.json",
    "start": "webpack --config config/webpack.config.js --watch --progress"
  },
  "babel": {
    "extends": "./config/.babelrc.json"
  },
  "eslintConfig": {
    "extends": "./config/.eslintrc.js"
  },
  "stylelint": {
    "extends": "./config/.stylelintrc.json"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Webpack

// config/webpack.config.js

const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  entry: {
    popup: path.resolve(__dirname, "../src/index.tsx"),
    background: path.resolve(__dirname, "../src/background.ts"),
  },
  plugins: [
    new CopyPlugin({
      patterns: [{ from: "public", to: "." }],
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.ts(x)?$/,
        loader: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.png$/,
        use: [
          {
            loader: "url-loader",
            options: {
              mimetype: "image/png",
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx"],
  },
  mode: "production",
  output: {
    path: path.resolve(__dirname, `../dist`),
    filename: "[name].js",
  },
};

Problem

I've tried redux-devtools-extension:

// src/index.tsx

import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
import { Provider } from "react-redux";
import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import rootReducer from "./store/reducers";

export const store = createStore(rootReducer, composeWithDevTools());

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

which shows:

No Store Found

I've also tried using remote-redux-devtools:

// src/index.tsx

import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
import { Provider } from "react-redux";
import { createStore } from "redux";
import devToolsEnhancer from "remote-redux-devtools";
import rootReducer from "./store/reducers";

export const store = createStore(rootReducer, devToolsEnhancer());

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

which seems more promising, but has no data in the store:

Remote Redux Window

When I run in the browser, using npm start (react-scripts start), I do see the store in the Redux DevTools extension, so I am not sure what the problem is.

Possible Idea

When I build with React (react-scripts build), I can see the store just fine. The issue is that I cannot use react as it's build times are too slow and using webpack seems like the most logical alternative. Here is the build output:

Working After React Build

Any ideas?

2

There are 2 best solutions below

0
SuperStormer On BEST ANSWER

Note: this answer was originally added by OP as an edit to their original question, I have just reposted it as an answer.

A server needs to be running and remote redux devtools should be used rather than redux devtools extension.

The best approach would be the following:

  1. Install npmjs.com/package/remotedev-server
  2. Add "remotedev": "remotedev --hostname=localhost --port=8080" npm script
  3. Run the above script to start a server
  4. Right click on popup, Redux DevTools > Open Remote DevTools (not inspect)
  5. Settings > Use Custom Local Server > Type in the hostname and port specified in the npm script.

Here is what my store looks like:

import { createStore } from "redux";
import { composeWithDevTools } from "remote-redux-devtools";
import rootReducer from "./store/reducers";

const composeEnhancers = composeWithDevTools({
  realtime: true,
  hostname: "localhost",
  port: 8080
});

export const store = createStore(rootReducer, composeEnhancers());

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);
9
HMR On

I think the problem is with webpack uglifying __REDUX_DEVTOOLS_EXTENSION_COMPOSE__ in the following code

You can try the following:

import { createStore, compose } from 'redux'
//same code as original but with bracket notation
const composeWithDevTools = (
  typeof window !== 'undefined' && window["__REDUX_DEVTOOLS_EXTENSION_COMPOSE__"] ?
    window["__REDUX_DEVTOOLS_EXTENSION_COMPOSE__"] :
    function() {
      if (arguments.length === 0) return undefined;
      if (typeof arguments[0] === 'object') return compose;
      return compose.apply(null, arguments);
    }
);
//your original code but using your composeWithDevTools 
export const store = createStore(rootReducer, composeWithDevTools());

You don't need to import composeWithDevTools from redux-devtools-extension as you've created it yourself but if this solves the problem you should create an issue requesting for bracket notation so uglify won't break the code.

To see what the compiled code is you can add a console log before your createStore: console.log("creating store") then you can disable source maps in chrome devtools (Command+Shift+P or Control+Shift+P) and then search for the console text creating store shortcut is: command+alt+f or control+shift+f)