如何使用Apollo-hooks创建utils以进行react-native

时间:2019-07-11 10:33:54

标签: react-native graphql apollo

我正在使用react-native / apollo-hooks / graphql上的两个令牌(accessToken,refreshToken)进行身份验证流程。并创建了用于updateTokens的阿波罗挂钩。而且我需要在不同的组件中使用此钩子,因为每次用户想要执行任何操作(例如发布帖子或发表评论)时,我都需要调用updateTokens apollo钩子。因此,我需要将updateTokens的所有逻辑带到一个单独的utils中,因为我不想重复自己,并在各处放置相同的代码。但是,当我用apollo挂钩制作单独的util文件时,它不起作用。有人可以解释一下我该怎么做!

import React from 'react'
import { View, Button, Text } from 'react-native'
import { useMutation } from 'react-apollo-hooks'
import gql from 'graphql-tag'
import * as Keychain from 'react-native-keychain'

const UPDATE_TOKENS = gql`
  mutation UpdateTokens($accessToken: String!, $refreshToken: String!) {
    updateTokens(data: { accessToken: $accessToken, refreshToken: $refreshToken }) {
      user {
        name
        phone
      }
      accessToken
      refreshToken
    }
  }
`

const HomeScreen = ({ navigation }) => {
  const update_tokens = useMutation(UPDATE_TOKENS)

  const updateTokens = (accessToken, refreshToken) => {
    console.log('accessToken', accessToken)
    console.log('refreshToken', refreshToken)
    update_tokens({
      variables: { accessToken, refreshToken },
      update: async (cache, { data }) => {
        const accessToken = data.updateTokens.accessToken
        const refreshToken = data.updateTokens.refreshToken
        await Keychain.setGenericPassword(accessToken, refreshToken)
      }
    }).then(() => console.log('We have new credentials'))
  }

  const getCredentials = async () => {
    const tokens = await Keychain.getGenericPassword()
    console.log('tokens', tokens)
    const keyChainAccessToken = tokens.username
    const keyChainRefreshToken = tokens.password
    console.log('keyChainAccessToken', keyChainAccessToken)
    console.log('keyChainRefreshToken', keyChainRefreshToken)
    updateTokens(keyChainAccessToken, keyChainRefreshToken)
  }

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button title="updateTokens" onPress={getCredentials} />
      <Button title="getCredentials" onPress={checkCredentials} />
      <Button title="SignOut" onPress={userSignOut} />
    </View>
  )
}
export { HomeScreen }

我可以将其放在这样的单独文件中,然后在不同的地方使用它吗?

import { useMutation } from 'react-apollo-hooks'
import gql from 'graphql-tag'
import * as Keychain from 'react-native-keychain'
import jwtDecode from 'jwt-decode'

const UPDATE_TOKENS = gql`
  mutation UpdateTokens($refreshToken: String!, $refreshTokenId: String!) {
    updateTokens(refreshToken: $refreshToken, refreshTokenId: $refreshTokenId) {
      user {
        name
        phone
      }
      accessToken
      refreshToken
    }
  }
`

const updateCredentials = () => {
  const update_tokens = useMutation(UPDATE_TOKENS)
  const updateTokens = (refreshToken, refreshTokenId) => {
    console.log('refreshToken', refreshToken)
    console.log('refreshTokenId', refreshTokenId)
    update_tokens({
      variables: { refreshToken, refreshTokenId },
      update: async (cache, { data }) => {
        const newAccessToken = data.updateTokens.accessToken
        const newRefreshToken = data.updateTokens.refreshToken
        const user = data.updateTokens.user
        await Keychain.setGenericPassword(newAccessToken, newRefreshToken)
      }
    }).then(() => console.log('We have new credentials'))
  }

  const getCredentials = async () => {
    try {
      const tokens = await Keychain.getGenericPassword()
      console.log('tokens', tokens)
      const keychainAccessToken = tokens.username
      const keychainRefreshToken = tokens.password
      const currentTime = Date.now() / 1000
      const decodeAccessToken = jwtDecode(keychainAccessToken)
      console.log('decodeAccessToken', decodeAccessToken)
      const keychainRefreshTokenId = decodeAccessToken.refreshTokenId
      console.log('keychainRefreshTokenId', keychainRefreshTokenId)

      if (decodeAccessToken.exp > currentTime) {
        console.log('Credentials is still valid')
      } else if (decodeAccessToken.exp < currentTime) {
        console.log('Go to Update!')
        updateTokens(keychainRefreshToken, keychainRefreshTokenId)
      }
    } catch (err) {
      throw new Error('Invalid credentials')
    }
  }
}
export { updateCredentials }

2 个答案:

答案 0 :(得分:0)

对我来说,我不会为了刷新令牌而这样做。我为此做了一个中间件。

如何设置中间件:

import { createUploadLink } from 'apollo-upload-client';
const getClient = ({ userStore: { token, refreshToken: refresh_token }, lang }, dispatch) => {
    const locale = lang || 'en';

    const uploadLink = createUploadLink({
        uri: 'http://localhost:4000'
    });

    const client = new ApolloClient({
        link: ApolloLink.from([
            getTokensMiddleware(token, refresh_token, locale, dispatch),
            uploadLink
        ]),
        cache: new InMemoryCache()
    });

    return client;
};

中间件:

const getTokensMiddleware = (token, refresh_token, locale, dispatch) => {
    return setContext(async (req, { headers, ...others }) => {
        if (!token || !refresh_token) return {};
        var decoded = jwtDecode(token);
        const isExpired = decoded.exp <= Date.now() / 1000 + 120;
        var decodedRefresh = jwtDecode(refresh_token);
        const isRefreshJWTExpired = decodedRefresh.exp <= Date.now() / 1000;
        if(isRefreshJWTExpired) return {};
        if (!isExpired) {
            return {
                ...others,
                headers: {
                    ...headers,
                    Authorization: token ? `Bearer ${token}` : '',
                    locale
                }
            };
        }
        return new Promise((success, fail) => {
            refreshToken(refresh_token)
                .then(response => {
                    if (response.ok) {
                        return response.json();
                    } else {
                        fail(response);
                    }
                })
                .then(json => {
                    const { token } = json.data.refreshToken;
                    dispatch({type: "login", payload: json.data.refreshToken})
                    success({
                        ...others,
                        headers: {
                            ...headers,
                            Authorization: token ? `Bearer ${token}` : '',
                            locale
                        }
                    });
                });
        });
    });
};

您的刷新功能:

const refreshToken = refreshToken => {
    const data = {
        operation: 'RefreshTokenMutation',
        query:
            'mutation RefreshTokenMutation($email: String!, $refreshToken: String!) {  refreshToken(email: $email, refreshToken: $refreshToken) {    token    refreshToken    user {      id      email      username      displayname      role {        title        permissions        __typename      }      __typename    }    __typename  }}',
        variables: { email: 'admin_email', refreshToken: refreshToken }
    };
    return fetch('http://localhost:4000/', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(data)
    });
};

使用方法

const AppPage = () => {
    const { state, dispatch } = useStore(); //a hook for getting context containing useReducer
    return (
        <I18nextProvider i18n={i18n}>
            <Helmet>
                <title>My Project</title>
                <meta name="description" content="My project" />
            </Helmet>
            <ApolloProvider client={getClient(state, dispatch)}>
                <LocaleProvider locale={state.antdLocale}>
                    <App />
                </LocaleProvider>
            </ApolloProvider>
        </I18nextProvider>
    );
};

答案 1 :(得分:0)

如果您仍然希望这样做,可以采用HOC方式进行。

import React from 'react';
import YOUR_FUNCTIONS from '~/functions_location';

const withRefreshToken = () => (Component) => {
    return <Component {...YOUR_FUNCTIONS} />
};

export {
    withRefreshToken
}

之后,您可以这样做:

export default withRefreshToken(HomeScreen);