【发布时间】:2015-04-03 13:28:05
【问题描述】:
假设我有代码可以为上一页选择的选择框设置状态:
this.setState({selectedOption: 5});
有什么方法可以在页面刷新后将this.state.selectedOption 填充为 5?
我可以使用回调将其保存在 localStorage 中,然后执行一个大的 setState 还是有一种标准的方法来做这样的事情?
【问题讨论】:
标签: javascript reactjs
假设我有代码可以为上一页选择的选择框设置状态:
this.setState({selectedOption: 5});
有什么方法可以在页面刷新后将this.state.selectedOption 填充为 5?
我可以使用回调将其保存在 localStorage 中,然后执行一个大的 setState 还是有一种标准的方法来做这样的事情?
【问题讨论】:
标签: javascript reactjs
所以我的解决方案是在设置状态时也设置localStorage,然后在getInitialState 回调中再次从localStorage 获取值,如下所示:
getInitialState: function() {
var selectedOption = localStorage.getItem( 'SelectedOption' ) || 1;
return {
selectedOption: selectedOption
};
},
setSelectedOption: function( option ) {
localStorage.setItem( 'SelectedOption', option );
this.setState( { selectedOption: option } );
}
我不确定这是否可以被视为Anti-Pattern,但除非有更好的解决方案,否则它会起作用。
【讨论】:
${this.props.id}.SelectedOption
localStorage 替换为sessionStorage,您会为每个选项卡获得单独的状态,从而避免选项卡之间的冲突
您可以按照 Omar 的建议使用本地存储“持久化”状态,但是一旦设置了状态就应该这样做。为此,您需要将回调传递给setState 函数,并且需要对放入本地存储的对象进行序列化和反序列化。
constructor(props) {
super(props);
this.state = {
allProjects: JSON.parse(localStorage.getItem('allProjects')) || []
}
}
addProject = (newProject) => {
...
this.setState({
allProjects: this.state.allProjects.concat(newProject)
},() => {
localStorage.setItem('allProjects', JSON.stringify(this.state.allProjects))
});
}
【讨论】:
我们有一个允许用户在页面中设置“参数”的应用程序。我们所做的是在 URL 上设置这些参数,使用 React Router(结合 History)和一个库,该库将 JavaScript 对象 URI 编码为可用作查询字符串的格式。
当用户选择一个选项时,我们可以将其值推送到当前路由:
history.push({pathname: 'path/', search: '?' + Qs.stringify(params)});
pathname 可以是当前路径。在您的情况下,params 看起来像:
{
selectedOption: 5
}
然后在 React 树的顶层,React Router 将使用 location.search 的 prop 更新该组件的 props,这是我们之前设置的编码值,因此在 componentWillReceiveProps 中会有类似:
params = Qs.parse(nextProps.location.search.substring(1));
this.setState({selectedOption: params.selectedOption});
然后该组件及其子组件将使用更新的设置重新渲染。由于信息位于 URL 上,因此可以将其添加为书签(或通过电子邮件发送 - 这是我们的用例),并且刷新将使应用程序保持相同的状态。 这对我们的应用程序非常有效。
反应路由器:https://github.com/reactjs/react-router
历史:https://github.com/ReactTraining/history
查询字符串库:https://github.com/ljharb/qs
【讨论】:
我认为 state 仅用于查看信息,并且应该在 view state 之外持续存在的数据最好存储为 props。当您希望能够链接到某个页面或将 URL 共享到应用程序的深处,但否则会使地址栏变得混乱时,URL 参数很有用。
看看 Redux-Persist(如果你正在使用 redux)https://github.com/rt2zz/redux-persist
【讨论】:
如果存在,则从 localStorage 加载 state:
constructor(props) {
super(props);
this.state = JSON.parse(localStorage.getItem('state'))
? JSON.parse(localStorage.getItem('state'))
: initialState
覆盖this.setState以在每次更新后自动保存状态:
const orginial = this.setState;
this.setState = function() {
let arguments0 = arguments[0];
let arguments1 = () => (arguments[1], localStorage.setItem('state', JSON.stringify(this.state)));
orginial.bind(this)(arguments0, arguments1);
};
【讨论】:
使用 Hooks 和 sessionStorage:
const [count, setCount] = useState(1);
useEffect(() => {
setCount(JSON.parse(window.sessionStorage.getItem("count")));
}, []);
useEffect(() => {
window.sessionStorage.setItem("count", count);
}, [count]);
return (
<div className="App">
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
如果您仍然喜欢,请将 sessionStorage 替换为 localStorage。
【讨论】:
count 更改时运行
带钩子:
const MyComponent = () => {
const [selectedOption, setSelectedOption] = useState(1)
useEffect(() => {
const storedSelectedOption = parseInt(sessionStorage.getItem('selectedOption') || '1')
setSelectedOption(storedSelectedOption)
}, [])
const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedOption(parseInt(e.target.value))
sessionStorage.setItem('selectedOption', e.target.value)
}
return (
<select onChange={handleOnChange}>
<option value="5" selected={selectedOption === 5}>Five</option>
<option value="3" selected={selectedOption === 3}>Three</option>
</select>
)
}
显然这也有效:
const MyComponent = () => {
const [selectedOption, setSelectedOption] = useState<number>(() => {
return parseInt(sessionStorage.getItem('selectedOption') || '1')
})
const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedOption(parseInt(e.target.value))
sessionStorage.setItem('selectedOption', e.target.value)
}
return (
<select onChange={handleOnChange}>
<option value="5" selected={selectedOption === 5}>Five</option>
<option value="3" selected={selectedOption === 3}>Three</option>
</select>
)
}
在现代浏览器中:
return (
<select onChange={handleOnChange} value={selectedOption}>
<option value="5">Five</option>
<option value="3">Three</option>
</select>
)
对于输入 something 这样应该可以工作:
(回复@TanviAgarwal 评论中的问题)
const MyInput = React.forwardRef((props, ref) => {
const { name, value, onChange } = props
const [inputValue, setInputValue] = React.useState(() => {
return sessionStorage.getItem(name) || value || ''
})
const handleOnChange = (e) => {
onChange && onChange(e)
setInputValue(e.target.value)
sessionStorage.setItem(name, e.target.value)
}
return <input ref={ref} {...props} value={inputValue} onChange={handleOnChange} />
})
(使用 typescript 会稍微复杂一些,但并不可怕)
这将“永远”存储值,因此您必须以某种方式处理重置表单。
【讨论】:
我可能迟到了,但 react-create-app 的实际代码用于 react > 16 版本。每次更改状态都保存在 sessionStorage(不是 localStorage)中并通过 crypto-js 加密。刷新时(当用户要求通过单击刷新按钮刷新页面时)从存储中加载状态。我还建议不要在构建中使用 sourceMaps 以避免关键短语的可读性。
我的index.js
import React from "react";
import ReactDOM from "react-dom";
import './index.css';
import App from './containers/App';
import * as serviceWorker from './serviceWorker';
import {createStore} from "redux";
import {Provider} from "react-redux"
import {BrowserRouter} from "react-router-dom";
import rootReducer from "./reducers/rootReducer";
import CryptoJS from 'crypto-js';
const key = CryptoJS.enc.Utf8.parse("someRandomText_encryptionPhase");
const iv = CryptoJS.enc.Utf8.parse("someRandomIV");
const persistedState = loadFromSessionStorage();
let store = createStore(rootReducer, persistedState,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
function loadFromSessionStorage() {
try {
const serializedState = sessionStorage.getItem('state');
if (serializedState === null) {
return undefined;
}
const decrypted = CryptoJS.AES.decrypt(serializedState, key, {iv: iv}).toString(CryptoJS.enc.Utf8);
return JSON.parse(decrypted);
} catch {
return undefined;
}
}
function saveToSessionStorage(state) {
try {
const serializedState = JSON.stringify(state);
const encrypted = CryptoJS.AES.encrypt(serializedState, key, {iv: iv});
sessionStorage.setItem('state', encrypted)
} catch (e) {
console.log(e)
}
}
ReactDOM.render(
<BrowserRouter>
<Provider store={store}>
<App/>
</Provider>
</BrowserRouter>,
document.getElementById('root')
);
store.subscribe(() => saveToSessionStorage(store.getState()));
serviceWorker.unregister();
【讨论】: