Electron双窗口Redux Store数据同步问题咨询
Hey there! I totally get why this is confusing—you’re sharing the same Redux store import across two Electron windows, but state changes in one don’t reflect in the other. Let’s break down why this happens and how to fix it.
Why This Happens
Electron uses separate renderer processes for each BrowserWindow. Even though you’re importing the same store.js file in both windows, each renderer process creates its own isolated instance of the store. These instances don’t share memory, so modifying one has no effect on the other.
Solution 1: Sync State via Electron IPC (Inter-Process Communication)
The simplest approach is to use Electron’s IPC system to broadcast Redux actions from one window to all others. Here’s how to implement it:
Step 1: Update Main Process (main.js)
First, we’ll set up the main process to listen for actions from renderers and forward them to all other windows:
const { app, BrowserWindow, ipcMain } = require('electron'); let allWindows = []; function createWindow(windowPath) { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, contextIsolation: false // Adjust if using newer Electron versions (use preload scripts if needed) } }); win.loadFile(windowPath); allWindows.push(win); // Listen for Redux actions from renderers ipcMain.on('forward-redux-action', (event, action) => { // Send the action to every window except the one that sent it allWindows.forEach(window => { if (window.webContents !== event.sender) { window.webContents.send('sync-redux-action', action); } }); }); } app.whenReady().then(() => { createWindow('./firstWindow/index.html'); createWindow('./secondWindow/index.html'); });
Step 2: Modify Redux Store (redux/store.js)
Next, we’ll wrap the store’s dispatch method to send actions to the main process, and listen for synced actions from the main process to update the local store:
import { createStore } from 'redux'; import rootReducer from './reducers'; const { ipcRenderer } = window.require('electron'); const store = createStore(rootReducer); // Listen for synced actions from main process and dispatch locally ipcRenderer.on('sync-redux-action', (_, action) => { store.dispatch(action); }); // Override dispatch to send actions to main process first const originalDispatch = store.dispatch; store.dispatch = (action) => { const result = originalDispatch(action); ipcRenderer.send('forward-redux-action', action); return result; }; export default store;
Solution 2: Host Redux Store in Main Process (More Robust)
For a cleaner, single-source-of-truth approach, host the Redux store directly in the main process. Renderers will only dispatch actions to the main process and sync their local state with the main store.
Step 1: Main Process with Central Store (main.js)
const { app, BrowserWindow, ipcMain } = require('electron'); const { createStore } = require('redux'); const rootReducer = require('./redux/reducers').default; // Create the single source-of-truth store in main process const mainStore = createStore(rootReducer); let allWindows = []; function createWindow(windowPath) { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, contextIsolation: false } }); win.loadFile(windowPath); allWindows.push(win); // Respond to renderer requests for current state ipcMain.on('get-main-state', (event) => { event.returnValue = mainStore.getState(); }); // Handle action dispatches from renderers ipcMain.on('dispatch-to-main', (_, action) => { mainStore.dispatch(action); // Notify all renderers of the updated state allWindows.forEach(window => { window.webContents.send('main-state-updated', mainStore.getState()); }); }); } app.whenReady().then(() => { createWindow('./firstWindow/index.html'); createWindow('./secondWindow/index.html'); });
Step 2: Renderer Store Proxy (redux/store.js)
import { createStore } from 'redux'; import rootReducer from './reducers'; const { ipcRenderer } = window.require('electron'); // Initialize local store with state from main process const initialState = ipcRenderer.sendSync('get-main-state'); const store = createStore(rootReducer, initialState); // Sync local state when main store updates ipcRenderer.on('main-state-updated', (_, newState) => { // Dispatch a sync action to update local state store.dispatch({ type: 'SYNC_FROM_MAIN', payload: newState }); }); // Override dispatch to send actions to main process const originalDispatch = store.dispatch; store.dispatch = (action) => { // Skip sync actions to avoid loops if (action.type !== 'SYNC_FROM_MAIN') { ipcRenderer.send('dispatch-to-main', action); } return originalDispatch(action); }; // Add a reducer case to handle SYNC_FROM_MAIN (update your rootReducer) // Example in your testPage reducer: // case 'SYNC_FROM_MAIN': // return action.payload.testPage; export default store;
Quick Fix for Your Existing Button Logic
I noticed a small issue in your button handler:
<button onClick={()=>dispatch(addNumberAction(++changeNumber))}>{number}</button>
Instead of using ++changeNumber (which relies on a local variable), use number + 1 to ensure you’re always using the latest state from the store:
<button onClick={()=>dispatch(addNumberAction(number + 1))}>{number}</button>
内容的提问来源于stack exchange,提问作者verb to be

