AsyncStorage поведение - PullRequest
0 голосов
/ 03 мая 2018

Я столкнулся со странным поведением AsyncStorage, которое я не мог обернуть вокруг себя и был бы признателен любому, кто может объяснить мне, что происходит за кулисами (т.е. неудачные случаи и почему)

Вот код, над которым я работаю:

componentDidMount() {
        let _this = this;

        AsyncStorage.getItem('token', (err, data) => {
            setTimeout(() => {
                if(data !== null){
                    this.setState({isReady: true, isLoggedIn: true});
                    store.dispatch({type: t.LOGGED_IN, token: data});
                }
                else{
                    this.setState({isReady: true, isLoggedIn: false})
                    store.dispatch({type: t.LOGGED_OUT});
                }
            }, 3000)
            console.log(err);
        });
    }

Как вы можете видеть, я передаю функцию callback в getItem() согласно документации, которая в основном говорит мне, входил ли пользователь ранее и не выходил ли с тех пор (т.е. токен все еще сохраняется в устройстве / приложении где-то). Этот код завершился успешно с первого раза, получив старый токен, который я сохранил с помощью редуктора:

export default function authReducer(state = initialState, action)
{
    switch (action.type) {
        case t.LOGGED_IN:{
            AsyncStorage.setItem('token', action.token);
            return Object.assign({}, state, { isLoggedIn: true, token: action.token });
        }
        case t.LOGGED_OUT:{
            AsyncStorage.removeItem('token');
            return Object.assign({}, state, {isLoggedIn: false, token: null});
        }
        default:
            return state;
    }
}

Однако, во второй раз, когда я перезагружал приложение, AsyncStorage всегда не сможет получить данные, даже после того, как я попытался войти снова и снова.

Я также пробовал варианты вызовов AsyncStorage, то есть с использованием await, .then плюс .catch, но все они приводят к одному и тому же результату.

Мои вопросы:

  1. В случаях сбоя у меня сложилось впечатление, что getItem() будет по-прежнему вызывать функцию обратного вызова, которую я передал, поскольку в списке параметров есть error. Тем не менее, мой console.log никогда не запускался в вышеуказанном случае. Я ожидаю чего-то, чего не должен быть здесь?

  2. Почему это может продолжаться только со второго раза? Есть ли случай, когда вызов setItem() для одного и того же ключа более одного раза без его удаления вызовет сбой хранилища? (Я точно знаю, что первая попытка была успешной, потому что я распечатал полученный токен из асинхронного хранилища)

  3. Это как-то связано с тем, что я загружаю свое приложение из Expo и инициализирую приложение с CRNA? это как-то изменит качество сохраняемости asyncStorage?

спасибо заранее! :)

РЕДАКТИРОВАТЬ: при дальнейшей проверке кажется, что остановка упаковщика и запуск его снова, кажется, позволяет приложению еще раз успешно получить старый токен, но если я обновлю приложение снова, после редактирования кода, getItem() будет снова провал. Так ли это с Экспо и постоянным хранением?

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Редукторы должны быть свободны от побочных эффектов , так как это чистая функция.

Как указано в документах

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

Побочные эффекты - когда процедура изменяет переменную вне ее области действия

Лучшим подходом было бы использовать redux-saga , , как отдельный поток для вашей модели побочных эффектов .

0 голосов
/ 04 мая 2018

Итак, проблема исправлена ​​благодаря моему более опытному другу-программисту. Оказывается, моя ошибка в том, что я поставил вызов AsyncStorage.setItem() в редуктор, который, по его словам, имеет deterministic характер. Я переместил вызов в actions.js этого класса прямо перед отправкой, и он работает!

Так что вместо

export default function authReducer(state = initialState, action)
{
    switch (action.type) {
        case t.LOGGED_IN:{
            AsyncStorage.setItem('token', action.token);
            return Object.assign({}, state, { isLoggedIn: true, token: action.token });
        }
        case t.LOGGED_OUT:{
            AsyncStorage.removeItem('token');
            return Object.assign({}, state, {isLoggedIn: false, token: null});
        }
        default:
            return state;
    }
}

Я сделал

export default function authReducer(state = initialState, action)
{
    switch (action.type) {
        case t.LOGGED_IN:{
            return Object.assign({}, state, { isLoggedIn: true, token: action.token });
        }
        case t.LOGGED_OUT:{
            return Object.assign({}, state, {isLoggedIn: false, token: null});
        }
        default:
            return state;
    }
}

Плюс это

export function login(data, successCB, errorCB) {
    return (dispatch) => {
        api.login(data, function (success, data, error) {
            if (success && data.exists) {
                AsyncStorage.setItem('token', data.token); //NOTE THIS
                dispatch({type: t.LOGGED_IN, token: data.token});
                successCB(data);
            }else if (error) errorCB(error)
        });
    };
}

export function signOut(successCB, errorCB){
  return (dispatch) => {
    AsyncStorage.removeItem('token'); //NOTE THIS
    dispatch({type: t.LOGGED_OUT});
    successCB();
  }
}

Но мой вопрос все еще persists (простите за каламбур): «Почему эта простая модификация работает? Я понял механизм reducers и dispatchers здесь неправильно?»

Кроме того, что значит быть deterministic и иметь отношение к несовместимости с Async звонками?

Если кто-нибудь сможет объяснить мне эту концепцию, это будет здорово! : D

...