如何在Redux中使用React Native异步存储?

时间:2018-09-16 07:50:36

标签: javascript reactjs react-native redux

我已执行登录和注销操作以及userReducer。如何将异步存储与redux集成?我正在使用redux thunk作为中间件。

我能够使用内部状态变量来实现登录和注销,但是我无法理解如何将其分解为动作和reducer,以及如何利用异步存储来存储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中正确实现了异步存储?

3 个答案:

答案 0 :(得分:2)

要保持Redux状态,我建议您redux-persist

安装:

npm i -S redux-persist

用法:

首先,配置redux存储

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

答案 1 :(得分:1)

您可以方便地单独使用AsyncStorage或redux来管理身份验证状态。取决于您是否适应。我会给你一个例子。

对于AsyncStorage: 假设您具有仅在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';

在您的还原动作中

//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的商店中,套用applyMiddleWare。让我们称之为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来管理登录-比我在这里概述的要好。我认为您也可以使用redux来管理登录状态,而无需使用AsyncStorage。您需要做的只是在loginScreen中,如果登录函数返回response.status =='ok',则可以将操作分派到redux以登录用户。在上面的示例中,使用asyncstorage您可能只需要使用redux检查用户是否已登录。

答案 2 :(得分:0)

建议您在AsyncStorage之上而不是AsyncStorage之上直接使用抽象,以实现除光使用之外的其他用途,因为它在全球范围内运行。 Redux-persist是AsyncStorage之上的抽象。它提供了一种更好的方式来存储和检索更复杂的数据(例如redux-persist具有persistentReducer(),persistStore())。

反应本机打字稿实现

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;