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