Как создать utils с помощью хуков apollo для react-native

Я создаю поток аутентификации с двумя токенами (accessToken, refreshToken) на react-native/apollo-hooks/graphql. И создал apollo-hook для updateTokens. И этот хук мне нужно использовать в разных компонентах, потому что каждый раз, когда пользователь хочет выполнить какие-либо действия, такие как создание публикации или комментария, мне нужно вызвать хук updateTokens apollo. Поэтому мне нужно вынести всю логику updateTokens в отдельный utils, потому что я не хочу повторяться и ставить везде один и тот же код. Но когда я делаю отдельный файл утилиты с хуком аполлона, он не работает. Может кто-нибудь объяснить мне, как я могу это сделать, пожалуйста!

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.info('accessToken', accessToken)
    console.info('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.info('We have new credentials'))
  }

  const getCredentials = async () => {
    const tokens = await Keychain.getGenericPassword()
    console.info('tokens', tokens)
    const keyChainAccessToken = tokens.username
    const keyChainRefreshToken = tokens.password
    console.info('keyChainAccessToken', keyChainAccessToken)
    console.info('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.info('refreshToken', refreshToken)
    console.info('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.info('We have new credentials'))
  }

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

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

Умерло ли Create-React-App?
Умерло ли Create-React-App?
В этом документальном фильме React.dev мы исследуем, мертв ли Create React App (CRA) и какое будущее ждет этот популярный фреймворк React.
Освоение React Native: Пошаговое руководство для начинающих
Освоение React Native: Пошаговое руководство для начинающих
React Native - это популярный фреймворк с открытым исходным кодом, используемый для разработки мобильных приложений. Он был разработан компанией...
В чем разница между react native и react ?
В чем разница между react native и react ?
React и React Native - два популярных фреймворка для создания пользовательских интерфейсов, но они предназначены для разных платформ. React - это...
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
Если вы уже умеете работать с React, создание мобильных приложений для iOS и Android - это новое приключение, в котором вы сможете применить свои...
Хуки React: что это такое и как их использовать
Хуки React: что это такое и как их использовать
Хуки React - это мощная функция библиотеки React, которая позволяет разработчикам использовать состояние и другие возможности React без написания...
0
0
271
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Для меня я не буду делать это для обновления токена. Я сделал промежуточное программное обеспечение для него.

Как установить промежуточное ПО:

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

Большое спасибо за отличный ответ, но не могли бы вы объяснить, откуда вы берете эти аргументы при создании промежуточного программного обеспечения: { userStore: { token, refreshToken: refresh_token }, lang }, dispatch? В моем случае это должно быть хранилище связки ключей?

jocoders 11.07.2019 15:40

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

Horst 12.07.2019 05:28

Не могли бы вы ответить, пожалуйста, еще на один вопрос. Когда работает Apollo setContext? В момент инициализации приложения или позже? Потому что в моем случае мне нужно сохранить токен в setContext, но когда приложение инициализируется, в хранилище связки ключей нет токенов, потому что пользователь еще не зарегистрировался. Он получает токены только после входа в систему. И когда он вышел из системы, токены стираются. Но я считаю, что setContext начинает работать с самого начала. Это правильно?

jocoders 12.07.2019 08:18

getTokensMiddleware — это функция для создания промежуточного программного обеспечения. Это начинается, когда вы устанавливаете ApolloClient. А getClient — это функция, генерирующая клиент apollo. Мы используем токены (состояние) для создания нового клиента во время работы приложения. Как только вы обновите токены (состояние), он сгенерирует новый клиент. Обновлен ответ для примера.

Horst 12.07.2019 08:57

Где вы храните хук useReducer? Можете ли вы прислать мне пример, пожалуйста

jocoders 12.07.2019 10:17
gist.github.com/fattomhk/1aca0b9b8834e1598db71673b292b065 . user.js — ленивый пример, измените эти локальные хранилища на цепочку для ключей или другой безопасный способ.
Horst 12.07.2019 12:19

мой код слишком ленив, чтобы в реальном случае вы могли подумать о редукции или другом инструменте управления состоянием

Horst 12.07.2019 12:22

Но я не понимаю, как я могу создать поля в userInitialState в user.js с помощью async/await? Связка ключей работает только с async/await

jocoders 12.07.2019 16:36

Вы можете указать пустую строку, а затем обновить ее с помощью «отправки».

Horst 13.07.2019 07:35

если вы все еще хотите это сделать, вы можете сделать это в режиме HOC.

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

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

export {
    withRefreshToken
}

после этого вы можете сделать это так:

export default withRefreshToken(HomeScreen);

Другие вопросы по теме