React On Rails集成Redux切换页面后丢失持久化Store问题求助
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:
- Duplicate Redux Stores: You're creating a new
storeinstance directly inside theScratch1component. Every time this component mounts, it spins up a fresh store—so state isn't shared across components, and navigating away resets everything. - 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

