如何防止 redux-persist 在用户允许之前使用 LocalStorage?
How to prevent redux-persist from using LocalStorage before it has been allowed by the user?
我想在使用 LocalStorage 之前通过单击横幅按钮来获得用户的同意。 LocalStorage 通过 redux-persist
使用。我使用 redux
和 redux-persist
如下:
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persitor} >
<MyAppRouter />
</PersistGate>
</Provider>,
document.getElementById("root")
)
和store
和persistor
来自
import { createStore } from "redux"
import { persistStore, persistReducer } from "redux-persist"
import storage from "redux-persist/lib/storage"
import { rootReducer } from "../reducers/index"
const persistConfig = {
key: "root",
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer)
const persistor = persistStore(store as any)
// `as any` is necessary as long as https://github.com/reduxjs/redux/issues/2709 is not fixed
export { store, persistor }
redux-persist
在创建对象后立即将初始状态保存在 LocalStorage 中,这不是我想要的。
无论状态是否持久化,我都想在我的 React 组件中使用 redux。
我认为 reducer 的细节无关紧要,因为它不会影响 where/how 状态的存储。
用户曾经表示同意使用LocalStorage和cookies的信息存储在cookie中。
仅在用户在首次访问期间表示同意或 cookie 已经存在后,我如何才能开始使用 LocalStorage 进行持久化? cookie过期或已被删除的情况应包含在首次访问的情况下。
为了尽量减少讨论,我正在寻找解决技术问题的解决方案。就法律要求而言,该请求可能有些矫枉过正。
到目前为止我尝试过的事情:
- 使用两个存储(可能是不好的做法),它们在
App
组件的状态下进行管理,该组件包装 Provider
,如示例所示。问题是点击横幅按钮后页面会立即重新呈现,这是不可接受的。
- 已评估的黑名单将不起作用,因为它对状态的初始持久性没有影响。
[1] https://softwareengineering.stackexchange.com/questions/290566/is-localstorage-under-the-cookie-law
这里的想法是,redux-persist 的持久化组件负责在 localStorage 中持久化数据或 redux 状态。
为了根据用户的同意做出决定,您需要有条件地渲染或不渲染 PersistGate
组件
要解决这样的问题,您可以在 Persistor 上编写一个自定义组件,仅在授予权限时才呈现它。提示用户授予或拒绝权限的逻辑也可以放在同一个组件中
例子
class PermissionAndPersist extends React.Component {
constructor(props) {
super(props)
this.state = {
permission: this.getCookie('userLocalStoragePermission')
}
}
getCookie(name) {
//implement the getc ookie method using document.cookie or a library
/* state returned must have the following syntax
isPermissionGranted,
isCookieExpired
*/
// The above syntax can be determined based on whether cookie is present as well as by checking the expiry data if cookie was present
}
render() {
const { permission } = this.state;
const {children, persistor} = this.props;
if(!permission.isPermissionGranted) {
// no permission granted, return a plain div/Fragment
return (
<React.Fragment>
{children}
</React.Fragment>
)
}
if(permission.isCookieExpired) {
return <Modal>{/* here goes a component which asks for user permission and on click updates the state as well as update in cookie */}</Modal>
}
// now if the cookie is present and permission is granted and cookie is not expired you render the `PersistGate` component
return <PersistGate persistor={persitor} >{children}</PersistGate>
}
}
如上创建组件后,您将按如下方式呈现它
ReactDOM.render(
<Provider store={store}>
<PermissionAndPersist persistor={persitor} >
<MyAppRouter />
</PermissionAndPersist >
</Provider>,
document.getElementById("root")
)
注意:您始终可以根据要求修改 PermissionAndPersist 组件的实现,但请注意,PeristGate 必须仅在所有条件都匹配时才呈现。如果用户未授予权限,您可能还想清除 localStorage
编辑: 由于要求实际上不是在单击用户横幅时重新呈现整个应用程序,因此我们需要进行一些更改。
首先,根据条件重新渲染ModalComponent。其次,我们不能有条件地改变我们重新渲染的组件,否则整个应用程序都会被刷新。到目前为止,实现它的唯一方法是实际实现自己在 localStorage 中持久化 redux 状态的逻辑,并在刷新时首先获取它
class PermissionAndPersist extends React.Component {
constructor(props) {
super(props)
this.state = {
permission: this.getCookie('userLocalStoragePermission')
}
}
getCookie(name) {
//implement the getc ookie method using document.cookie or a library
/* state returned must have the following syntax
isPermissionGranted,
isCookieExpired
*/
// The above syntax can be determined based on whether cookie is present as well as by checking the expiry data if cookie was present
}
componenDidMount() {
const { permission } = this.state;
const { dispatch } = this.props;
if(permission.isPermissionGranted && !permission.isCookieExpired) {
// Idea here is to populate the redux store based on localStorage value
const state= JSON.parse(localStorage.get('REDUX_KEY'));
dispatch({type: 'PERSISTOR_HYDRATE', payload: state})
}
// Adding a listner on window onLoad
window.addEventListener('unload', (event) => {
this.persistStateInLocalStorage();
});
}
persistStateInLocalStorage = () => {
const { storeState} = this.props;
const {permission} = this.state;
if(permission.isPermissionGranted && !permission.isCookieExpired) {
localStorage.set('REDUX_KEY', JSON.stringify(storeState))
}
}
componentWillUnmount() {
this.persistStateInLocalStorage();
}
render() {
const {children} = this.props;
const {permission} = this.state;
return (
<React.Fragment>
{children}
{permission.isCookieExpired ? <Modal>{/*Pemission handling here*/}</Modal>}
</React.Fragment>
)
}
const mapStateToProps = (state) => {
return {
storeState: state
}
}
export default connect(mapStateToProps)(PermissionAndPersist);
一旦你实现了上面的组件,你需要在 reducer 中监听 PERSISTOR_HYDRATE
动作并更新 redux 状态。
注意:您可能需要添加更多处理以使持久性和正确补水,但想法保持不变
类似下面的内容是我能想到的最好的。我找不到阻止 redux-persist 持久化的方法,但是使用自定义序列化程序,有可能导致它在获得同意之前持久化一个空字符串。因此,在您同意之前,您将在您的存储中获得 persist:root
= ''
。
// cookieConsent.js
import CookieConsent from "@grrr/cookie-consent"
export const BASIC_FUNCTIONALITY = 'basic-functionality';
export const cookieConsent = CookieConsent({
...
cookies: [
{
id: BASIC_FUNCTIONALITY,
...
},
...
]
});
// configureStore.js
import {cookieConsent, BASIC_FUNCTIONALITY} from './cookieConsent'
...
export default function configureStore(initialState) {
...
const serialize = (data) => {
return cookieConsent.isAccepted(BASIC_FUNCTIONALITY) ? JSON.stringify(data) : ''
}
const persistedReducer = persistReducer({
key: 'root',
storage,
serialize,
whitelist: config.reduxPersistWhitelist,
}, createRootReducer(history))
const store = createStore(...)
const persistor = persistStore(store)
cookieConsent.on('update', (cookies) => {
// Just persist on every update to the consent; depend on the serializer to do the right thing
persistor.persist()
})
...
}
我想在使用 LocalStorage 之前通过单击横幅按钮来获得用户的同意。 LocalStorage 通过 redux-persist
使用。我使用 redux
和 redux-persist
如下:
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persitor} >
<MyAppRouter />
</PersistGate>
</Provider>,
document.getElementById("root")
)
和store
和persistor
来自
import { createStore } from "redux"
import { persistStore, persistReducer } from "redux-persist"
import storage from "redux-persist/lib/storage"
import { rootReducer } from "../reducers/index"
const persistConfig = {
key: "root",
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer)
const persistor = persistStore(store as any)
// `as any` is necessary as long as https://github.com/reduxjs/redux/issues/2709 is not fixed
export { store, persistor }
redux-persist
在创建对象后立即将初始状态保存在 LocalStorage 中,这不是我想要的。
无论状态是否持久化,我都想在我的 React 组件中使用 redux。
我认为 reducer 的细节无关紧要,因为它不会影响 where/how 状态的存储。
用户曾经表示同意使用LocalStorage和cookies的信息存储在cookie中。
仅在用户在首次访问期间表示同意或 cookie 已经存在后,我如何才能开始使用 LocalStorage 进行持久化? cookie过期或已被删除的情况应包含在首次访问的情况下。
为了尽量减少讨论,我正在寻找解决技术问题的解决方案。就法律要求而言,该请求可能有些矫枉过正。
到目前为止我尝试过的事情:
- 使用两个存储(可能是不好的做法),它们在
App
组件的状态下进行管理,该组件包装Provider
,如示例所示。问题是点击横幅按钮后页面会立即重新呈现,这是不可接受的。 - 已评估的黑名单将不起作用,因为它对状态的初始持久性没有影响。
[1] https://softwareengineering.stackexchange.com/questions/290566/is-localstorage-under-the-cookie-law
这里的想法是,redux-persist 的持久化组件负责在 localStorage 中持久化数据或 redux 状态。
为了根据用户的同意做出决定,您需要有条件地渲染或不渲染 PersistGate
组件
要解决这样的问题,您可以在 Persistor 上编写一个自定义组件,仅在授予权限时才呈现它。提示用户授予或拒绝权限的逻辑也可以放在同一个组件中
例子
class PermissionAndPersist extends React.Component {
constructor(props) {
super(props)
this.state = {
permission: this.getCookie('userLocalStoragePermission')
}
}
getCookie(name) {
//implement the getc ookie method using document.cookie or a library
/* state returned must have the following syntax
isPermissionGranted,
isCookieExpired
*/
// The above syntax can be determined based on whether cookie is present as well as by checking the expiry data if cookie was present
}
render() {
const { permission } = this.state;
const {children, persistor} = this.props;
if(!permission.isPermissionGranted) {
// no permission granted, return a plain div/Fragment
return (
<React.Fragment>
{children}
</React.Fragment>
)
}
if(permission.isCookieExpired) {
return <Modal>{/* here goes a component which asks for user permission and on click updates the state as well as update in cookie */}</Modal>
}
// now if the cookie is present and permission is granted and cookie is not expired you render the `PersistGate` component
return <PersistGate persistor={persitor} >{children}</PersistGate>
}
}
如上创建组件后,您将按如下方式呈现它
ReactDOM.render(
<Provider store={store}>
<PermissionAndPersist persistor={persitor} >
<MyAppRouter />
</PermissionAndPersist >
</Provider>,
document.getElementById("root")
)
注意:您始终可以根据要求修改 PermissionAndPersist 组件的实现,但请注意,PeristGate 必须仅在所有条件都匹配时才呈现。如果用户未授予权限,您可能还想清除 localStorage
编辑: 由于要求实际上不是在单击用户横幅时重新呈现整个应用程序,因此我们需要进行一些更改。
首先,根据条件重新渲染ModalComponent。其次,我们不能有条件地改变我们重新渲染的组件,否则整个应用程序都会被刷新。到目前为止,实现它的唯一方法是实际实现自己在 localStorage 中持久化 redux 状态的逻辑,并在刷新时首先获取它
class PermissionAndPersist extends React.Component {
constructor(props) {
super(props)
this.state = {
permission: this.getCookie('userLocalStoragePermission')
}
}
getCookie(name) {
//implement the getc ookie method using document.cookie or a library
/* state returned must have the following syntax
isPermissionGranted,
isCookieExpired
*/
// The above syntax can be determined based on whether cookie is present as well as by checking the expiry data if cookie was present
}
componenDidMount() {
const { permission } = this.state;
const { dispatch } = this.props;
if(permission.isPermissionGranted && !permission.isCookieExpired) {
// Idea here is to populate the redux store based on localStorage value
const state= JSON.parse(localStorage.get('REDUX_KEY'));
dispatch({type: 'PERSISTOR_HYDRATE', payload: state})
}
// Adding a listner on window onLoad
window.addEventListener('unload', (event) => {
this.persistStateInLocalStorage();
});
}
persistStateInLocalStorage = () => {
const { storeState} = this.props;
const {permission} = this.state;
if(permission.isPermissionGranted && !permission.isCookieExpired) {
localStorage.set('REDUX_KEY', JSON.stringify(storeState))
}
}
componentWillUnmount() {
this.persistStateInLocalStorage();
}
render() {
const {children} = this.props;
const {permission} = this.state;
return (
<React.Fragment>
{children}
{permission.isCookieExpired ? <Modal>{/*Pemission handling here*/}</Modal>}
</React.Fragment>
)
}
const mapStateToProps = (state) => {
return {
storeState: state
}
}
export default connect(mapStateToProps)(PermissionAndPersist);
一旦你实现了上面的组件,你需要在 reducer 中监听 PERSISTOR_HYDRATE
动作并更新 redux 状态。
注意:您可能需要添加更多处理以使持久性和正确补水,但想法保持不变
类似下面的内容是我能想到的最好的。我找不到阻止 redux-persist 持久化的方法,但是使用自定义序列化程序,有可能导致它在获得同意之前持久化一个空字符串。因此,在您同意之前,您将在您的存储中获得 persist:root
= ''
。
// cookieConsent.js
import CookieConsent from "@grrr/cookie-consent"
export const BASIC_FUNCTIONALITY = 'basic-functionality';
export const cookieConsent = CookieConsent({
...
cookies: [
{
id: BASIC_FUNCTIONALITY,
...
},
...
]
});
// configureStore.js
import {cookieConsent, BASIC_FUNCTIONALITY} from './cookieConsent'
...
export default function configureStore(initialState) {
...
const serialize = (data) => {
return cookieConsent.isAccepted(BASIC_FUNCTIONALITY) ? JSON.stringify(data) : ''
}
const persistedReducer = persistReducer({
key: 'root',
storage,
serialize,
whitelist: config.reduxPersistWhitelist,
}, createRootReducer(history))
const store = createStore(...)
const persistor = persistStore(store)
cookieConsent.on('update', (cookies) => {
// Just persist on every update to the consent; depend on the serializer to do the right thing
persistor.persist()
})
...
}