redux-persists 不与反应本机应用程序一起工作
redux-persists not working with react native app
问题
重新启动应用程序时,商店未保留或以某种方式重置
预期行为
该应用程序基本上只有一个登录表单,其中会触发一些 redux 操作。例如,用户名和密码都保存在商店中。
用 Reactotron 检查后,商店确实按预期发展,用户名和密码以及其他字段都已更新。
技术栈
使用 Redux 的 React Native 应用程序(不是 Expo)
package.json
"dependencies": {
"react": "16.6.1",
"react-native": "0.57.7",
"react-native-gesture-handler": "^1.0.12",
"react-native-i18n": "^2.0.15",
"react-navigation": "^3.0.8",
"react-redux": "^6.0.0",
"redux": "^4.0.1",
"redux-persist": "^5.10.0"
},
申请状态
我创建了一个简单的 ReactNative 应用,其中的根组件是:
App.js
import React, {Component} from 'react';
import { Provider } from 'react-redux';
import LoadingIndicator from '@components/Visual/LoadingIndicator/';
import MainNavigator from '@components/Visual/MainNavigator'
import {checkUserState} from '@actions/GlobalActions'
import {store, persistor} from '@components/Storage/StorageConfigurationBuilder'
import { PersistGate } from 'redux-persist/integration/react'
export default class App extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
store.dispatch(checkUserState())
}
render() {
return (
<Provider store={store}>
<PersistGate loading={<LoadingIndicator />} persistor={persistor}>
<MainNavigator initialRoute={this.props.initialRoute} />
</PersistGate>
</Provider>
);
}
}
导入中的 @components
和 @actions
由 babel 插件解析 "module-resolver"
.babelrc
...
["module-resolver", {
"root": ["."],
"alias": {
"^@constants/(.+)": "./constants/\1",
"@i18n": "./i18n/index.js",
"^@components/(.+)": "./components/\1",
"^@screens/(.+)": "./screens/\1",
"^@reducers/(.+)": "./reducers/\1",
"^@actions/(.+)": "./actions/\1",
}
}]
...
路由应用中导入的 StorageConfigurationBuilder 提供了 store 和 persistor 很简单:
StorageConfigurationBuilder.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage/index.native'
import AllReducers from '@reducers/AllReducers'
//import Reactotron from '../../ReactronConfig'
const persistConfig = {
key: 'root',
storage
}
const persistedReducer = persistReducer(persistConfig, AllReducers)
export const store = createStore(persistedReducer)
export const persistor = persistStore(store)
最后是正在渲染的视图组件,分为两部分
index.js
import React, {Component} from 'react'
import { connect } from 'react-redux';
import LoginView from './LoginView';
import {loginFetch, loginFetchError, loginFetchSuccess} from '@actions/UserActions'
import ApiConfiguration, { getApiBaseUri } from '@constants/ApiConfiguration'
class LoginController extends Component {
constructor(props) {
super(props)
}
doLogin() {
this.props.dispatch(loginFetch());
fetch(getApiBaseUri() + ApiConfiguration.endpoints.authentication.path, {
...ApiConfiguration.headers,
method: ApiConfiguration.endpoints.authentication.method,
body: JSON.stringify({
email: this.props.email,
password: this.props.password
})
}).then((response) => {
return response.json()
}).then((response) => {
this.props.dispatch(loginFetchSuccess(response))
}).catch((e) => {
this.props.dispatch(loginFetchError(e))
})
}
render() {
return <LoginView {...this.props} login={this.doLogin.bind(this)} />;
}
}
const mapStateToProps = (state) => {
const { user } = state
return { user }
};
export default connect(mapStateToProps)(LoginController);
视图本身<LoginView />
没有什么特别之处
LoginView.js
import React from 'react';
import {StyleSheet, SafeAreaView, View, TextInput, Text, TouchableOpacity} from 'react-native';
import {changeEmail, changePassword} from '@actions/UserActions'
import I18n from '@i18n';
import Colors from '@constants/Colors';
export default props => {
return (<SafeAreaView style={styles.container}>
<View style={styles.loginForm}>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>{I18n.t('login.email_label')}</Text>
<TextInput
value={props.user.email}
onChangeText={value => props.dispatch(changeEmail(value))}
placeholder={I18n.t('login.email_placeholder')}
style={styles.field}
textContentType={"username"}
returnKeyType={"next"}
underlineColorAndroid={"transparent"}></TextInput>
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>{I18n.t('login.password_label')}</Text>
<TextInput
value={props.user.password}
onChangeText={value => props.dispatch(changePassword(value))}
style={styles.field}
placeholder={I18n.t('login.password_placeholder')}
textContentType={"password"}
underlineColorAndroid={"transparent"}
secureTextEntry={true}></TextInput>
</View>
<View style={styles.panelFooter}>
<TouchableOpacity onPress={() => props.login()}>
<Text>
{I18n.t('login.login_button')}
</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.backgroundColor,
justifyContent: 'center'
},
loginForm: {
marginLeft: 20,
marginRight: 20,
justifyContent: 'space-around',
borderWidth: 1,
borderColor: Colors.borderColor,
borderRadius: 5
},
panelFooter: {
flexDirection: 'row',
justifyContent: 'flex-end',
backgroundColor: '#e5e5e5',
height: 44,
paddingRight: 20,
paddingTop: 13,
marginTop: 35
},
fieldContainer: {
height: 50,
flexDirection: 'column',
marginLeft: 20,
marginRight: 20,
marginTop: 25
},
fieldLabel: {
fontSize: 13,
fontWeight: "600",
marginBottom: 4,
marginLeft: 10
},
field: {
height: 44,
borderWidth: 1,
borderColor: Colors.borderColor,
borderRadius: 22,
fontSize: 18,
paddingLeft: 22,
paddingRight: 10
}
})
所以我添加了一个简单的 reducer/action 并将其附加到我的应用程序,与上述相同的应用程序。我从我之前创建的另一个 standalone project 获得了这段代码,它工作正常。
这部分,FriendReducer 继续正常工作,而我上面描述的减速器仍然没有工作。
最后我在 reducer 中添加了以下案例来恢复我的应用程序状态。由于某种原因,它没有自己做。
case 'persist/REHYDRATE':
//rehydrating userStore to app
newState = { ...action.payload.userStore }
return newState
问题
重新启动应用程序时,商店未保留或以某种方式重置
预期行为
该应用程序基本上只有一个登录表单,其中会触发一些 redux 操作。例如,用户名和密码都保存在商店中。
用 Reactotron 检查后,商店确实按预期发展,用户名和密码以及其他字段都已更新。
技术栈
使用 Redux 的 React Native 应用程序(不是 Expo)
package.json
"dependencies": {
"react": "16.6.1",
"react-native": "0.57.7",
"react-native-gesture-handler": "^1.0.12",
"react-native-i18n": "^2.0.15",
"react-navigation": "^3.0.8",
"react-redux": "^6.0.0",
"redux": "^4.0.1",
"redux-persist": "^5.10.0"
},
申请状态
我创建了一个简单的 ReactNative 应用,其中的根组件是:
App.js
import React, {Component} from 'react';
import { Provider } from 'react-redux';
import LoadingIndicator from '@components/Visual/LoadingIndicator/';
import MainNavigator from '@components/Visual/MainNavigator'
import {checkUserState} from '@actions/GlobalActions'
import {store, persistor} from '@components/Storage/StorageConfigurationBuilder'
import { PersistGate } from 'redux-persist/integration/react'
export default class App extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
store.dispatch(checkUserState())
}
render() {
return (
<Provider store={store}>
<PersistGate loading={<LoadingIndicator />} persistor={persistor}>
<MainNavigator initialRoute={this.props.initialRoute} />
</PersistGate>
</Provider>
);
}
}
导入中的 @components
和 @actions
由 babel 插件解析 "module-resolver"
.babelrc
...
["module-resolver", {
"root": ["."],
"alias": {
"^@constants/(.+)": "./constants/\1",
"@i18n": "./i18n/index.js",
"^@components/(.+)": "./components/\1",
"^@screens/(.+)": "./screens/\1",
"^@reducers/(.+)": "./reducers/\1",
"^@actions/(.+)": "./actions/\1",
}
}]
...
路由应用中导入的 StorageConfigurationBuilder 提供了 store 和 persistor 很简单:
StorageConfigurationBuilder.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage/index.native'
import AllReducers from '@reducers/AllReducers'
//import Reactotron from '../../ReactronConfig'
const persistConfig = {
key: 'root',
storage
}
const persistedReducer = persistReducer(persistConfig, AllReducers)
export const store = createStore(persistedReducer)
export const persistor = persistStore(store)
最后是正在渲染的视图组件,分为两部分
index.js
import React, {Component} from 'react'
import { connect } from 'react-redux';
import LoginView from './LoginView';
import {loginFetch, loginFetchError, loginFetchSuccess} from '@actions/UserActions'
import ApiConfiguration, { getApiBaseUri } from '@constants/ApiConfiguration'
class LoginController extends Component {
constructor(props) {
super(props)
}
doLogin() {
this.props.dispatch(loginFetch());
fetch(getApiBaseUri() + ApiConfiguration.endpoints.authentication.path, {
...ApiConfiguration.headers,
method: ApiConfiguration.endpoints.authentication.method,
body: JSON.stringify({
email: this.props.email,
password: this.props.password
})
}).then((response) => {
return response.json()
}).then((response) => {
this.props.dispatch(loginFetchSuccess(response))
}).catch((e) => {
this.props.dispatch(loginFetchError(e))
})
}
render() {
return <LoginView {...this.props} login={this.doLogin.bind(this)} />;
}
}
const mapStateToProps = (state) => {
const { user } = state
return { user }
};
export default connect(mapStateToProps)(LoginController);
视图本身<LoginView />
没有什么特别之处
LoginView.js
import React from 'react';
import {StyleSheet, SafeAreaView, View, TextInput, Text, TouchableOpacity} from 'react-native';
import {changeEmail, changePassword} from '@actions/UserActions'
import I18n from '@i18n';
import Colors from '@constants/Colors';
export default props => {
return (<SafeAreaView style={styles.container}>
<View style={styles.loginForm}>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>{I18n.t('login.email_label')}</Text>
<TextInput
value={props.user.email}
onChangeText={value => props.dispatch(changeEmail(value))}
placeholder={I18n.t('login.email_placeholder')}
style={styles.field}
textContentType={"username"}
returnKeyType={"next"}
underlineColorAndroid={"transparent"}></TextInput>
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>{I18n.t('login.password_label')}</Text>
<TextInput
value={props.user.password}
onChangeText={value => props.dispatch(changePassword(value))}
style={styles.field}
placeholder={I18n.t('login.password_placeholder')}
textContentType={"password"}
underlineColorAndroid={"transparent"}
secureTextEntry={true}></TextInput>
</View>
<View style={styles.panelFooter}>
<TouchableOpacity onPress={() => props.login()}>
<Text>
{I18n.t('login.login_button')}
</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.backgroundColor,
justifyContent: 'center'
},
loginForm: {
marginLeft: 20,
marginRight: 20,
justifyContent: 'space-around',
borderWidth: 1,
borderColor: Colors.borderColor,
borderRadius: 5
},
panelFooter: {
flexDirection: 'row',
justifyContent: 'flex-end',
backgroundColor: '#e5e5e5',
height: 44,
paddingRight: 20,
paddingTop: 13,
marginTop: 35
},
fieldContainer: {
height: 50,
flexDirection: 'column',
marginLeft: 20,
marginRight: 20,
marginTop: 25
},
fieldLabel: {
fontSize: 13,
fontWeight: "600",
marginBottom: 4,
marginLeft: 10
},
field: {
height: 44,
borderWidth: 1,
borderColor: Colors.borderColor,
borderRadius: 22,
fontSize: 18,
paddingLeft: 22,
paddingRight: 10
}
})
所以我添加了一个简单的 reducer/action 并将其附加到我的应用程序,与上述相同的应用程序。我从我之前创建的另一个 standalone project 获得了这段代码,它工作正常。
这部分,FriendReducer 继续正常工作,而我上面描述的减速器仍然没有工作。
最后我在 reducer 中添加了以下案例来恢复我的应用程序状态。由于某种原因,它没有自己做。
case 'persist/REHYDRATE':
//rehydrating userStore to app
newState = { ...action.payload.userStore }
return newState