redux-persists не работает с собственным приложением React

Проблема

Хранилище не сохраняется или каким-то образом сбрасывается при перезапуске приложения.

Ожидаемое поведение

Приложение имеет в основном просто форму входа в систему, в которой запускаются некоторые действия redux. Например, в магазине сохраняются имя пользователя и пароль.

После проверки с помощью Reactotron, магазин фактически эволюционирует, как и ожидалось, обновляются имя пользователя и пароль, а также другие поля.

Технический стек

Приложение React Native (не Expo) с Redux

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, импортированный в приложение маршрута, которое предоставляет как хранилище, так и персистор, просто:

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
    }
})

person 500 Server error    schedule 19.12.2018    source источник
comment
Вы запускаете приложение на Android? IOS? или оба? Возникает ли проблема как в IOS, так и в Android?   -  person nebuler    schedule 20.12.2018
comment
Я пробовал iOS 12 на симуляторах iPhone X и iPhone 6   -  person 500 Server error    schedule 20.12.2018


Ответы (1)


Поэтому я добавил простой редуктор / действие и прикрепил его к своему приложению, то же самое, что описано выше. Я получил этот код из другого автономного проекта, созданного мной ранее, который работал нормально.

В этой части FriendReducer продолжал нормально работать, в то время как описанные выше редукторы все еще не работали.

Наконец, я добавил в редуктор следующий случай, чтобы восстановить состояние моего приложения. По какой-то причине он не делал этого самостоятельно.

case 'persist/REHYDRATE':
   //rehydrating userStore to app
   newState = { ...action.payload.userStore }
   return newState
person 500 Server error    schedule 21.12.2018