React Redux状态在页面刷新时丢失

20

在我的React应用程序中,我有3个组件。从第一个组件通过一个按钮,我使用Link去到第二个组件。在第二个组件中,我创建了一个状态(Redux存储),对其进行操作,并在提交按钮完成操作后重定向到第三个组件。在第三个组件中,我也可以看到该状态(通过Redux Chrome工具),但是当我刷新页面时(即代码更改并保存时的Webpack操作),我会失去状态并获得一个空对象。

这是我的应用程序结构。

index.js

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunk))
);

ReactDOM.render(
  <BrowserRouter>
    <Provider store={store}>
      <Route component={App} />
    </Provider>
  </BrowserRouter>,
  document.getElementById("root")
);

App.js

const App = ({ location }) => (
  <Container>
    <Route location={location} path="/" exact component={HomePage} />
    <Route location={location} path="/GameInit" exact component={GameInit} />
    <Route location={location} path="/Battle" exact component={Battle} />
  </Container>
);

App.propTypes = {
  location: PropTypes.shape({
    pathname: PropTypes.string.isRequired
  }).isRequired
};

export default App;

我在主页上有一个按钮,链接到GameInit

const HomePage = () => (<Button color="blue" as={Link} to="/GameInit">START GAME</Button>);
export default HomePage;

游戏初始化页面如下所示

class GameInit extends Component {
  componentWillMount() {
    const { createNewPlayer} = this.props;
    createNewPlayer();
  }
/* state manipulation till it gets valid */
 palyerIsReady()=>{ history.push("/Battle");}
 
export default connect(mapStateToProps,{ createNewPlayer})(GameInit);

最后是战斗组件,我首次看到状态,但在刷新后失去了。

class Battle extends Component {
  render() {return (/*some stuff*/);}}

Battle.propTypes = {
  players: PropTypes.object.isRequired // eslint-disable-line
};
const mapStateToProps = state => ({
  players: state.players
});
export default connect(mapStateToProps,null)(Battle);

1
这是很自然的事情... - Bhojendra Rauniyar
1
@BhojendraRauniyar和解决方案? - Amir-Mousavi
3
Thunk不是中间件来处理这个吗?如果我想将所有内容持久化到本地存储中,为什么还要使用redux呢? - Amir-Mousavi
1
你可以使用redux-persist。 - Bhojendra Rauniyar
1
准备初始状态并通过createStore(combined, { a: 'horse' })传递它。https://redux.js.org/recipes/structuringreducers/initializingstate。从我的角度来看,这是有道理的。 - Michal Cholewiński
显示剩余4条评论
6个回答

28

您可以轻松地将其保留在本地存储中。请查看下面的示例。

const loadState = () => {
  try {
    const serializedState = localStorage.getItem('state');
    if(serializedState === null) {
      return undefined;
    }
    return JSON.parse(serializedState);
  } catch (e) {
    return undefined;
  }
};

const saveState = (state) => {
  try {
    const serializedState = JSON.stringify(state);
    localStorage.setItem('state', serializedState);
  } catch (e) {
    // Ignore write errors;
  }
};

const peristedState = loadState();

store.subscribe(() => {
  saveState(store.getState());
});

const store = createStore(
  persistedState,
  // Others reducers...
);

render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root');
);

序列化是一项昂贵的操作。您应该使用节流函数(例如由lodash实现的函数)来限制保存的数量。

例如:

import throttle from 'lodash/throttle';

store.subscribe(throttle(() => {
  saveState(store.getState());
}, 1000));

我应该写composeWithDevTools(persistedState)来查看Chrome扩展的状态吗? - Amir-Mousavi
它不会改变您现有代码的任何内容。createStore 的第二个参数只是初始状态。您仍然可以添加thunk并插入Chrome扩展程序。 - Alexandre Annic
persistedState应该被放置在enhancer中吗?就像这样: createStore( rootReducer, composeWithDevTools(applyMiddleware(thunk)), persistedState ); - Amir-Mousavi
1
啊哈,我懂了,非常感谢。对于我的情况来说是最好的解决方案 :) - Amir-Mousavi
1
@Temi'Topsy'Bello,我认为代码中存在错误,你应该在store声明之后写入store.subscribe(...) - Alexandre Annic
显示剩余2条评论

5

您可以使用类似redux-persist的工具将redux状态保存到本地存储(local storage)或其他存储系统中。


1

Alexender Annic 回答正确的方式:

// persist store code
const loadState = () => {
  try {
    const serializedState = localStorage.getItem('state');
    if(serializedState === null) {
      return undefined;
    }
    return JSON.parse(serializedState);
  } catch (e) {
    return undefined;
  }
};

const saveState = (state) => {
  try {
    const serializedState = JSON.stringify(state);
    localStorage.setItem('state', serializedState);
  } catch (e) {
    // Ignore write errors;
  }
};

const persistedState = loadState();

// This persistedState is includedat the time of store creation as initial value
const store = createStore(reducers, persistedState);

// This is actually call every time when store saved
store.subscribe(() => {
  saveState(store.getState());
});


export default store;

1
我的个人建议是使用React hot loader进行热重载,这样可以避免页面重新加载,只切换已更改的块。这意味着您的状态在开发过程中不会丢失。
安装非常简单。您只需要添加一个条目并将初始组件包装在hot中即可,该组件可以从该软件包中获取。
如果您需要更多关于其工作原理的信息,我建议观看此演讲

0

大家都推荐使用localStorage,但你也可以保存到sessionStorage(我读过比localStorage更安全),并在刷新时检查存储项。你可以在响应成功或其他地方设置它。

window.sessionStorage.setItem("sessionName",JSON.stringify(message.payload));

然后可以在componentDidUpdate或mount中检查。

  if(!isLoggedIn && !isAuthenticated && window.sessionStorage.getItem('session-name')){
            saveWhatYouWant(JSON.parse(window.sessionStorage.getItem('sessionName')))
        
        }

我发现这样做速度更快,而且你基本上是在不需要编写大量代码的情况下重新填充存储。


对于任何选择这种方式的人,需要注意:SessionStorage 在标签页之间不共享!因此,请确保您可以接受这种行为。 - Alex von Brandenfels

0
    import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import { store } from "./rootReducer";
import { BrowserRouter } from "react-router-dom";

if (process.env.NODE_ENV === "development" && module.hot) {
  module.hot.accept("./rootReducer", () => {
    const newRootReducer = require("./rootReducer").default;
    store.replaceReducer(newRootReducer);
  });
}

export type AppDispatch = typeof store.dispatch;

const render = () => {
  const App = require("./App").default;
  ReactDOM.render(
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>,
    document.getElementById("root"),
  );
};

render();

if (process.env.NODE_ENV === "development" && module.hot) {
  module.hot.accept("./App", render);
}

背景是什么?它是什么,它做什么,为什么要这样做?我们无法读取您的思维。能否详细说明一下? - Dariusz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接