简体   繁体   中英

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

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:

未找到商店

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:

远程 Redux 窗口

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:

在 React 构建之后工作

Any ideas?

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)

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")
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM