redux-persist-electron-storage icon indicating copy to clipboard operation
redux-persist-electron-storage copied to clipboard

Not loading page after reload

Open IvanSavoskin opened this issue 3 years ago • 0 comments

I want to use redux-persist-electron-storage to store some project data. I set everything up according to the instructions. To begin with, the application started to run for a very long time. Secondly, if you refresh the page in electron, then it does not load the page, just a white screen hangs. This does not open dev-tools, that is, it feels like the application has entered a loop somewhere. Is there any way to solve this problem?

store configuration

import { configureStore } from "@reduxjs/toolkit";
import ElectronStore from "electron-store";
import createElectronStorage from "redux-persist-electron-storage";
import persistStore from "redux-persist/es/persistStore";
import thunk from "redux-thunk";
import promise from "redux-promise-middleware";
import { persistReducer } from "redux-persist";
import { app } from "@electron/remote";
import tableReducer from "./table/tableReducer";
import sidebarMenuReducer from "./sidebarMenu/sidebarMenuReducer";
import electronCacheReducer from "./electronCache/electronCacheReducer";

export const electronStore = new ElectronStore({ cwd: app.getPath("userData") });

const electronCachePersistConfig = {
    key: "electronCache",
    storage: createElectronStorage({ electronStore })
};

const electronCachePersistedReducer = persistReducer(electronCachePersistConfig, electronCacheReducer);

export const store = configureStore({
    reducer: {
        tableData: tableReducer,
        sidebarMenu: sidebarMenuReducer,
        electronCache: electronCachePersistedReducer
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: [promise, thunk]
});

/* // @ts-ignore
module.hot.accept("./electronCache/electronCacheReducer", () => {
    // This fetch the new state of the above reducers.
    // @ts-ignore
    const nextElectronCacheReducer = require("./electronCache/electronCacheReducer").default;
    // @ts-ignore
    store.replaceReducer(
        {
            // @ts-ignore
            tableData: tableReducer,
            sidebarMenu: sidebarMenuReducer,
            electronCache: persistReducer(electronCachePersistConfig, nextElectronCacheReducer)
        }
    );
}); */

export const persistor = persistStore(store);
persistor.persist();

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

index.tsx

import * as React from "react";
import { render } from "react-dom";
import "./styles/globals.scss";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { Loader } from "semantic-ui-react";
import { persistor, store } from "./store";
import App from "./components/App";
import partsStyles from "./styles/parts.scss";

render((
    <Provider store={store}>
        <PersistGate
            loading={(
                <div className={partsStyles.loaderContainer}>
                    <Loader active inline="centered" />
                </div>
            )} persistor={persistor}
        >
            <App />
        </PersistGate>
    </Provider>
), document.querySelector("#app"));

webpack config for renderer

import path from "path";
import webpack, { Configuration } from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
import { merge } from "webpack-merge";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import createElectronReloadWebpackPlugin from "electron-reload-webpack-plugin";

export const PATHS = {
    src: path.join(__dirname, "./src"),
    dist: path.join(__dirname, "./app/dist"),
    global: path.resolve(__dirname, "./src/styles/globals.scss"),
    assets: "assets/",
    nodeModules: path.resolve(__dirname, "./node_modules"),
    app: path.resolve(__dirname, "./app")
};

const isProduction = process.env.NODE_ENV === "production";
const mode = isProduction ? "production" : "development";

module.exports = () => {
    const commonPlugins = [
        new HtmlWebpackPlugin({
            template: `${PATHS.src}/index.html`,
            filename: "./index.html",
            title: "Demo",
            publicPath: isProduction ? "./" : ""
        }),
        new CleanWebpackPlugin({
            cleanOnceBeforeBuildPatterns: []
        }),
        new webpack.ProgressPlugin(),
        new webpack.DefinePlugin({
            "process.env.NODE_ENV": JSON.stringify(mode),
            "process.env.DEBUG": JSON.stringify(process.env.DEBUG)
        }),
        new ForkTsCheckerWebpackPlugin()
    ];

    const devPlugins = [
        new webpack.SourceMapDevToolPlugin({
            filename: "[file].map"
        }),
        createElectronReloadWebpackPlugin()()
    ];

    const plugins = [...commonPlugins, ...(isProduction
        ? []
        : devPlugins)];

    const devOptions: Configuration = {
        devServer: {
            contentBase: PATHS.dist,
            port: 8085,
            overlay: {
                warnings: false,
                errors: true
            },
            hot: true,
            historyApiFallback: true
        }
    };

    const config: Configuration = {
        mode,
        target: "electron-renderer",
        devtool: isProduction ? "cheap-source-map" : "eval-cheap-module-source-map",
        context: __dirname,
        entry: {
            app: PATHS.src
        },
        resolve: {
            extensions: [".js", ".jsx", ".ts", ".tsx", "json", ".css", ".scss", ".svg", ".ignore"]
        },
        module: {
            rules: [{
                test: /\.scss$/,
                include: [PATHS.global],
                use: ["style-loader", "css-loader", "sass-loader"]
            }, {
                test: /\.scss$/,
                exclude: [/node_modules/, PATHS.global],
                use: [
                    "style-loader", {
                        loader: "css-loader",
                        options: {
                            import: true,
                            sourceMap: true,
                            modules: {
                                mode: "local",
                                localIdentName: "[path][name]__[local]--[hash]",
                                exportLocalsConvention: "camelCaseOnly"
                            }
                        }
                    }, {
                        loader: "postcss-loader",
                        options: {
                            sourceMap: true,
                            postcssOptions: {
                                config: path.resolve(__dirname, "postcss.config.js")
                            }
                        }
                    }, {
                        loader: "sass-loader",
                        options: {
                            sourceMap: true
                        }
                    }
                ]
            }, {
                test: /\.tsx?$/,
                exclude: [PATHS.nodeModules, PATHS.app],
                use:
                    [{
                        loader: "ts-loader",
                        options: {
                            compilerOptions: {
                                module: "ESNext",
                                removeComments: false
                            }
                        }
                    }]
            }, {
                test: /\.(ignore|zip|png|ttf|otf|eot|svg|woff(2)?)(\?[\da-z]+)?$/,
                use:
                    [{
                        loader: "file-loader",
                        options: {
                            name: "[name]-[contenthash].[ext]",
                            outputPath: "static/assets/"
                        }
                    }]
            }]
        },
        optimization: {
            splitChunks: {
                cacheGroups: {
                    vendor: {
                        name: "vendors",
                        test: /node_modules/,
                        chunks: "all",
                        enforce: true,
                        maxSize: 249_856
                    }
                }
            },
            runtimeChunk: "single",
            moduleIds: "deterministic"
        },
        output: {
            path: PATHS.dist,
            filename: `${PATHS.assets}js/[name]-bundle.[fullhash].js`,
            publicPath: isProduction ? "./" : "/"
        },
        plugins
    };

    return isProduction ? config : merge(config, devOptions);
};

webpack config for main

import path from "path";

export const PATHS = {
    electronMain: path.join(__dirname, "./app/main.ts"),
    dist: path.join(__dirname, "./app/dist")
};

const isProduction = process.env.NODE_ENV === "production";
const mode = isProduction ? "production" : "development";

module.exports = {
    mode,
    target: "electron-main",
    entry: {
        app: PATHS.electronMain
    },
    resolve: {
        extensions: [".js", ".ts"]
    },
    module: {
        rules: [
            {
                test: /app\/main\.ts$/,
                include: PATHS.electronMain,
                use:
                    [{
                        loader: "ts-loader",
                        options: {
                            compilerOptions: {
                                module: "commonjs",
                                removeComments: true
                            }
                        }
                    }]
            }
        ]
    },
    output: {
        path: PATHS.dist,
        filename: "main.js",
        publicPath: "/"
    }
};

main.ts

import electron from "electron";
import isElectronDev from "electron-is-dev";
import installExtension, { REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS } from "electron-devtools-installer";
import { client } from "electron-connect";
import { initialize as remoteInitialize } from "@electron/remote/main";

const {
    app,
    BrowserWindow,
    Menu
} = electron;

if (!isElectronDev) {
    app.on("browser-window-created", (e, window) => {
        if (process.platform === "darwin") {
            window.setMenu(Menu.buildFromTemplate([{ // иначе не работает CmdC, CmdV, CmdX
                label: "Edit",
                submenu: [
                    {
                        label: "Cut",
                        accelerator: "CmdOrCtrl+X"
                    },
                    {
                        label: "Copy",
                        accelerator: "CmdOrCtrl+C"
                    },
                    {
                        label: "Paste",
                        accelerator: "CmdOrCtrl+V"
                    }
                ]
            }]));
        } else {
            window.setMenu(null);
        }
    });
}

function loadMain() {
    const screenWidth = electron.screen.getPrimaryDisplay().size.width;
    const screenHeight = electron.screen.getPrimaryDisplay().size.height;

    remoteInitialize();

    const mainWindow = new BrowserWindow({
        width: 1366,
        height: 768,
        minWidth: 1366,
        minHeight: 768,
        show: false,
        webPreferences: {
            devTools: isElectronDev,
            nodeIntegration: true,
            contextIsolation: false,
            enableRemoteModule: true
        }
    });
    mainWindow.loadURL(isElectronDev ? "http://localhost:8085" : `file://${__dirname}/index.html`);
    mainWindow.once("ready-to-show", () => {
        if (isElectronDev) {
            client.create(mainWindow);
        }
        mainWindow.show();
    });
    if (screenWidth === 1366 && screenHeight === 768) {
        mainWindow.maximize();
        mainWindow.setResizable(false);
    } else {
        mainWindow.setResizable(true);
    }

    mainWindow.webContents.setWindowOpenHandler(() => ({ action: "deny" }));

    function close() {
        app.quit();
    }

    mainWindow.once("close", close);
}

app.on("window-all-closed", () => {
    app.quit();
});

app.on("quit", () => {
    app.quit();
});

app.whenReady()
    .then(() => {
        if (isElectronDev) {
            installExtension(REACT_DEVELOPER_TOOLS)
                .then((name) => console.log(`Added Extension:  ${name}`))
                .catch((error) => console.log("An error occurred:", error));
            installExtension(REDUX_DEVTOOLS)
                .then((name) => console.log(`Added Extension:  ${name}`))
                .catch((error) => console.log("An error occurred:", error));
        }

        loadMain();
    });

I am use

  • react - "17.0.2"
  • react-redux - "7.2.4"
  • redux-persist - "6.0.0",
  • redux-persist-electron-storage - 2.1.0
  • electron - 13.1.3
  • webpack - 5.38.1
  • electron-store - 8.0.0

IvanSavoskin avatar Jun 26 '21 21:06 IvanSavoskin