【问题标题】:How to use React Native AsyncStorage with Redux?如何将 React Native AsyncStorage 与 Redux 一起使用?
【发布时间】:2019-02-20 10:54:47
【问题描述】:

我已经进行了登录和注销操作以及 userReducer。如何将 AsyncStorage 与 Redux 集成?我使用 Redux Thunk 作为中间件。

我能够使用内部状态变量实现登录和注销,但我无法理解如何将其分解为 action 和 reducer,以及如何使用 AsyncStorage 来存储 accessToken

原代码:

_onLogin = () => {
    auth0.webAuth
      .authorize({
        scope: 'openid profile',
        audience: 'https://' + credentials.domain + '/userinfo'
      })
      .then(credentials => {
        this.setState({ accessToken: credentials.accessToken });
      })
      .catch(error => console.log(error));
  };

  _onLogout = () => {
    if (Platform.OS === 'android') {
      this.setState({ accessToken: null });
    } else {
      auth0.webAuth
        .clearSession({})
        .then(success => {
          this.setState({ accessToken: null });
        })
        .catch(error => console.log(error));
    }
  };

loginAction.js:

   import { LOGIN_USER } from './types';
import Auth0 from 'react-native-auth0';

var credentials = require('./auth0-credentials');
const auth0 = new Auth0(credentials);

export const loginUser = () => dispatch => {
    auth0.webAuth
    .authorize({
      scope: 'openid profile',
      audience: 'https://' + credentials.domain + '/userinfo'
    })
    .then(credentials =>
        dispatch({
            type: LOGIN_USER,
            payload: credentials.accessToken
        })
    )
    .catch(error => console.log(error));
}

logoutAction.js:

       import { LOGOUT_USER } from './types';
import Auth0 from 'react-native-auth0';

var credentials = require('./auth0-credentials');
const auth0 = new Auth0(credentials);

export const logoutUser = () => dispatch => {

        auth0.webAuth
          .clearSession({})
          .then(success => 
                dispatch({
                    type: LOGOUT_USER,
                    payload: null
                })
          )
          .catch(error => console.log(error));
}

userReducer.js:

  import { LOGIN_USER, LOGOUT_USER } from '../actions/types';

const initialState = {
    accessToken: null
}

export default function (state = initialState, action) {
    switch (action.type) {

        case LOGIN_USER:

            _storeData = async () => {
                try {
                    await AsyncStorage.setItem('accessToken', action.payload);
                } catch (error) {
                    console.log(error)
                }
            }

            return {
               ...state,
               accessToken:action.payload
            };

        case LOGOUT_USER:

            _removeData = async (accessToken) => {
                try {
                    await AsyncStorage.removeItem(accessToken);
                } catch (error) {
                    console.log(error)
                }
            }    

            return {
                ...state,
                accessToken:action.payload
            };

        default:
            return state;
    }
}

我是 Redux 的新手,所以我尝试将原始代码转换为操作和减速器,但我不确定我是否在 userReducer.js 中正确实现了 AsyncStorage?

【问题讨论】:

    标签: javascript reactjs react-native redux


    【解决方案1】:

    为了保持 redux 状态,我推荐你redux-persist

    安装:

    npm i -S redux-persist
    

    用法:

    首先,配置redux store

    // configureStore.js
    
    import { createStore } from 'redux'
    import { persistStore, persistReducer } from 'redux-persist'
    import storage from 'redux-persist/lib/storage' // defaults to localStorage for web and AsyncStorage for react-native
    
    import rootReducer from './reducers'
    
    const persistConfig = {
      key: 'root',
      storage,
    }
    
    const persistedReducer = persistReducer(persistConfig, rootReducer)
    
    export default () => {
      let store = createStore(persistedReducer)
      let persistor = persistStore(store)
      return { store, persistor }
    }
    

    然后,用PersistGate 包装你的根组件

    import { PersistGate } from 'redux-persist/integration/react'
    
    // ... normal setup, create store and persistor, import components etc.
    
    const App = () => {
      return (
        <Provider store={store}>
          <PersistGate loading={null} persistor={persistor}>
            <RootComponent />
          </PersistGate>
        </Provider>
      );
    };
    

    【讨论】:

    • 是否可以将 asyncstorage 与 redux 一起使用?我正在尝试使用 asyncstorage 来实现它
    • 我正在尝试将我的原始代码拆分为 action 和 reducer,但我无法使用 asyncstorage。
    • @funjoker storage 默认为 ‍‍AsyncStorage for react-native
    • 检查我更新的 loginAction.js 和 lougoutAction.js
    • 在 logoutAction.js 中应该是 -> payload: nullpayload: success.null 我想在单击注销按钮后将 accessToken 设置为 null
    【解决方案2】:

    您可以方便地单独使用 AsyncStorage 或 redux 来管理身份验证状态。取决于你对哪个感到舒服。我会给你一个两者的例子。

    对于异步存储: 假设您拥有仅有效期为 2 周的身份验证密钥。您可以在用户登录时记下并节省时间。例如:

    //LoginScreen
    import { onSignIn } from '../actions/auth'; //I will describe the onSignInMethod below
    import axios from 'axios'; //lets use axios. You may use fetch too.
    
    
    export default class LoginScreen extends Component {
    
    
        //your code: state, static etc
        loginMethod = () => {
            const url = yourauthUrl;
            const payload = {
                email: this.state.email,
                password: this.state.password
            };
            axios.post(url, payload)
            .then((response) => {
                if (response.status == 200) {
                    const dateOfLastLogin = new Date().getTime().toString(); //take note of the time the user logs in.
                    AsyncStorage.setItem('dateOfLastLogin', dateOfLastLogin);
                }
            })
            .then(() => { 
                onSignIn() //onSignIn handles your sign in. See below.
                .then(() => this.props.navigation.navigate('AfterSignInPage'));
                })
                .catch(() => { // your callback if onSignIn Fails
                });
            })
            .catch((error) => { //your callback if axios fails
            });
        }
    
    }
    

    ../actions/auth.js

    import { AsyncStorage } from 'react-native';
    
    export const onSignIn = () => AsyncStorage.setItem('auth_key', 'true');
    //in LoginScreen we called this to set that a user has successfully logged in
    //why is true a string? -- Because Asyncstorage stores only strings
    
    export const onSignOut = () => AsyncStorage.multiRemove(['auth_key', 'dateOfLastLogin']);
    
    //now lets create a method that checks if the user is logged in anytime
    export const isSignedIn = () => {
        return new Promise((resolve, reject) => {
            AsyncStorage.multiGet(['auth_key', 'dateOfLastLogin'])
            .then((res) => {
                const userKey = res[0][1];
                const lastLoginDate = parseInt(res[1][1]);
                const today = new Date().getTime();
                const daysElapsed = Math.round(
                    (today - lastLoginDate) / 86400000
                    );
                if (userKey !== null && (daysElapsed < 14)) {
                    resolve(true);
                } else {
                    resolve(false);
                }
            })
            .catch((err) => reject(err));
        });
    };
    

    现在我们可以从我们的任何组件中import { isSignedIn } from '../actions/auth'; 并像这样使用它:

    isSignedIn()
        .then((res) => {
            if (res) { 
                // user is properly logged in and the login keys are valid and less than 14 days 
            }
        })
    

    /////////////////////////////////////// /////////////////////////

    如果你想使用 redux

    在 redux 中处理登录

    在你的types.js

    //types.js
    export const LOGGED_IN = 'LOGGED_IN';
    

    在你的 redux 操作中

    //loginActions.js
    import {
        LOGGED_IN,
    } from './types';
    
    export function login() {
        let dateOfLastLogin = null;
        let isLoggedIn = 'false';
        AsyncStorage.multiGet(['auth_key', 'dateOfLastLogin'])
        .then((res) => {
            isLoggedIn = res[0][1];
            dateOfLastLogin = parseInt(res[1][1]);
        }); //note this works asynchronously so, this may not be a good approach
        return {
            type: LOGGED_IN,
            isLoggedIn, 
            dateOfLastLogin
        };
    }
    

    在你的 loginReducer 中

    //LoginReducer.js
    import {
        LOGGED_IN
    } from '../actions/types';
    
    
    const initialState = {
        userIsLoggedIn: false
    };
    
    export function loginReducer(state=initialState, action) {
        switch (action.type) {
    
            case LOGGED_IN:
    
                const userKey = action.isLoggedIn;
                const lastLoginDate = action.dateOfLastLogin;
                const today = new Date().getTime();
                const daysElapsed = Math.round(
                    (today - lastLoginDate) / 86400000
                    );
                let trulyLoggedIn = false;
                if (userKey !== null && (daysElapsed < 14)) {
                    trulyLoggedIn = true;
                } else { trulyLoggedIn = false }
                return {
                    userIsLoggedIn: trulyLoggedIn
                };
    
            default:
                return state;
        }
    }
    

    在你的./reducers/index.js

    //reducers index.js
    import { combineReducers } from 'redux';
    
    import { loginReducer } from './LoginReducers';
    
    const rootReducer = combineReducers({
        loggedIn: loginReducer
    });
    
    export default rootReducer;
    

    在您使用 redux-thunk 的商店中,应用MiddleWare。让我们称之为 configureStore.js

    //configureStore.js
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from '../reducers';
    
    export default function configureStore(initialState) {
        return createStore(
            rootReducer,
            initialState,
            applyMiddleware(thunk)
        );
    }
    

    在您的 App.js 中

    //App.js
    import { Provider } from 'react-redux';
    import configureStore from './src/store/configureStore'; //where you configured your store
    import { YourMainNavigator } from '../src/config/router'; //where your root navigator is
    
    const store = configureStore();
    export default class App extends Component<{}> {
        render() {
            return (
                <Provider store={store}>
                    <YourMainNavigator />
                </Provider>
            );
        }
    }
    

    您应该知道您不再需要 auth.js 中的 isSignedIn 方法 您的登录方法与上面在 LoginScreen 中概述的相同。

    现在你可以像这样使用 redux 来检查登录状态:

    import React, {Component} from 'react';
    import {connect} from 'react-redux';
    
    class MyComponent extends Component {
        someFunction() {
            if (this.props.loggedIn) {
                //do something
            }
        }
    }
    const mapStateToProps = (state) => {
        return {
            loggedIn: state.loggedIn.userIsLoggedIn
        };
    }
    
    
    export default connect(mapStateToProps)(MyComponent);
    

    应该有更好的方法来使用 redux 来管理登录 - 比我在这里概述的更好。我认为您也可以在不使用 AsyncStorage 的情况下使用 redux 来管理您的登录状态。你需要做的就是在你的 loginScreen 中,如果登录函数返回一个 response.status == 'ok',你可以向 redux 发送一个动作来让用户登录。在上面的例子中,使用 asyncstorage 你可能只需要使用 redux 检查用户是否登录。

    【讨论】:

    • 当我实现有问题的原始代码时,我得到了访问令牌。据我了解,您是说不要同时使用 asyncstorage 和 redux。只有 redux 或只有 asyncstorage 才能解决问题,我对吗?
    • 我正在关注本教程 -> snack.expo.io/@react-navigation/auth-flow
    • 您可以单独使用 asyncstorage。您可以将 asyncstorage 与 redux 一起使用。或者你可以只使用 redux。对于您在登录期间获得的访问令牌,您可以使用 asyncstorage 来存储它。如果您也收到这些,还包括客户端、到期和 uid。
    【解决方案3】:

    建议您在 AsyncStorage 之上使用抽象,而不是直接使用 AsyncStorage,因为它在全局范围内运行。 Redux-persist 是 AsyncStorage 之上的抽象。它提供了一种更好的方式来存储和检索更复杂的数据(例如 redux-persist 有 persistReducer()、persistStore())。

    React 原生 typescript 实现

    storage.ts

    import AsyncStorage from "@react-native-community/async-storage";
    import { createStore, combineReducers } from "redux";
    import { persistStore, persistReducer } from "redux-persist";
    
    import exampleReducer from "./example.reducer";
    
    const rootReducer = combineReducers({
      example: exampleReducer,
    });
    
    const persistConfig = {
      key: "root",
      storage: AsyncStorage,
      whitelist: ["example"],
    };
    
    
    // Middleware: Redux Persist Persisted Reducer
    const persistedReducer = persistReducer(persistConfig, rootReducer);
    
    const store = createStore(persistedReducer);
    
    // Middleware: Redux Persist Persister
    let persistor = persistStore(store);
    
    export { store, persistor };
    

    App.tsx

    import React from "react";
    import { PersistGate } from "redux-persist/es/integration/react";
    import { Provider } from "react-redux";
    
    import RootNavigator from "./navigation/RootNavigator";
    import { store, persistor } from "./store";
    
    function App() {
      return (
        <Provider store={store}>
          <PersistGate loading={null} persistor={persistor}>
            <RootNavigator />
          </PersistGate>
        </Provider>
      );
    }
    
    export default App;
    

    【讨论】:

      猜你喜欢
      • 2015-06-06
      • 1970-01-01
      • 2018-06-10
      • 1970-01-01
      • 1970-01-01
      • 2019-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多