You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React On Rails集成Redux切换页面后丢失持久化Store问题求助

Fixing Redux State Loss in React on Rails

Hey there! Let's break down why your shopping cart state is disappearing when navigating or refreshing, and walk through how to fix it.

What's Causing the Issue?

Looking at your code, two key problems are leading to lost state:

  1. Duplicate Redux Stores: You're creating a new store instance directly inside the Scratch1 component. Every time this component mounts, it spins up a fresh store—so state isn't shared across components, and navigating away resets everything.
  2. Full Page Refresh: Using window.location.assign('/cart') triggers a full page reload, which wipes out all in-memory Redux state entirely.

Step-by-Step Fixes

1. Create a Global Shared Store

First, move your store creation to a dedicated file so all components use the same single instance. This ensures state stays consistent across your entire app.

Create src/store/index.js:

import { createStore } from 'redux';
import rootReducer from '../reducers';

// Initialize one global store for the entire app
export const store = createStore(rootReducer);

// We'll add persistence for page refreshes in step 4

2. Use React Router for Navigation (Avoid Page Refreshes)

Instead of window.location.assign, use React Router's navigation methods to keep your app running as a single-page application (SPA)—this way, Redux state stays in memory instead of getting wiped on reload.

Update your component to use withRouter (or the useHistory hook if you prefer functional components):

// @flow
import * as React from 'react';
import { connect } from 'react-redux';
import { setCart } from 'action/SetCart';
import { withRouter, type History } from 'react-router-dom'; // Import React Router
import { store } from '../store'; // Import the global store

type Props = { 
  product: number,
  history: History, // Add history prop for navigation
  setCart: (number) => void,
  cart: Array<number>
}

export class Scratch1 extends React.Component<Props> {
 onClick = () => {
   this.props.setCart(this.props.product);
   // Navigate without reloading the page
   this.props.history.push('/cart');
 };

 render() {
   // Remove the Provider from this component—we'll add it at the app root
   return (
     <button onClick={this.onClick}>add to cart</button>
   );
 }
}

const Scratch1Redux = connect(
 state => ({
   cart: state.cart,
 }),
 dispatch => ({
   setCart: id => dispatch(setCart(id))
 })
)(withRouter(Scratch1)); // Wrap to access React Router's history

export default Scratch1Redux;

3. Add a Root Redux Provider

Wrap your entire React app with the Redux Provider at the top level (usually in your React on Rails startup file). This makes the global store available to all connected components.

Create src/startup/ServerApp.jsx:

import React from 'react';
import { Provider } from 'react-redux';
import { store } from '../store';
import { Route, Switch } from 'react-router-dom';
import Scratch1 from '../components/Scratch1'; // Your product detail component
import Cart from '../components/Cart'; // Your cart component

const App = () => (
  <Provider store={store}>
    <Switch>
      {/* Map your routes to React components */}
      <Route path="/product/:id" component={Scratch1} />
      <Route path="/cart" component={Cart} />
    </Switch>
  </Provider>
);

export default App;

4. Persist State for Page Refreshes (Optional but Recommended)

If you need state to survive full page refreshes (e.g., if parts of your app are traditional Rails pages), use redux-persist to save state to localStorage.

Update src/store/index.js with persistence:

import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // Uses localStorage by default
import rootReducer from '../reducers';

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['cart'] // Only persist the cart slice of state
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

export const store = createStore(persistedReducer);
export const persistor = persistStore(store);

Then update your root app component to include PersistGate:

import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from '../store';
import { Route, Switch } from 'react-router-dom';
import Scratch1 from '../components/Scratch1';
import Cart from '../components/Cart';

const App = () => (
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <Switch>
        <Route path="/product/:id" component={Scratch1} />
        <Route path="/cart" component={Cart} />
      </Switch>
    </PersistGate>
  </Provider>
);

export default App;

Final Quick Check

Make sure your Rails view uses the root React component correctly. For example:

<div class='fancyClass'> <%= react_component("ServerApp", props: { product: @product.id }) %> </div>

内容的提问来源于stack exchange,提问作者GYTO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:32:19