如何使用 apollo-hooks 为 react-native 创建实用程序
Posted
技术标签:
【中文标题】如何使用 apollo-hooks 为 react-native 创建实用程序【英文标题】:How to create utils with apollo-hooks for react-native 【发布时间】:2019-11-21 01:22:12 【问题描述】:我正在 react-native/apollo-hooks/graphql 上使用两个令牌(accessToken、refreshToken)进行身份验证流程。并为 updateTokens 创建了 apollo-hook。这个钩子我需要在不同的组件中使用,因为每次用户想要做任何动作,比如发帖或评论时,我都需要调用 updateTokens apollo 钩子。因此我需要将 updateTokens 的所有逻辑放到一个单独的工具中,因为我不想重复自己并将相同的代码放在任何地方。但是当我用 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
【问题讨论】:
【参考方案1】:对我来说,我不会为了刷新令牌而这样做。我为它做了一个中间件。
如何设置中间件:
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?在我的情况下,它必须来自钥匙串存储? 这些变量仅供我自己使用,您可以使用自己的方式。它们来自用于获取和更新令牌状态的 useReducer 钩子。 您能否再回答一个问题。 Apollo setContext 何时工作?在应用程序的初始化时刻还是之后?因为在我的情况下,我需要将令牌保存到 setContext,但是当应用程序初始化时,钥匙串存储没有任何令牌,因为用户尚未登录。他只有在登录后才能获得令牌。当他注销时,令牌正在擦除。但我的东西 setContext 是从一开始就开始工作的。对吗? getTokensMiddleware 是生成中间件的函数。它在您设置 ApolloClient 时启动。 getClient 是生成 apollo 客户端的函数。我们使用令牌(状态)在应用程序运行期间生成一个新客户端。更新令牌(状态)后,它将生成一个新客户端。更新了 ans 示例。 你把 useReducer 钩子放在哪里?你能给我一个例子吗?【参考方案2】:如果你还想这样做,你可以用 HOC 的方式来做。
import React from 'react';
import YOUR_FUNCTIONS from '~/functions_location';
const withRefreshToken = () => (Component) =>
return <Component ...YOUR_FUNCTIONS />
;
export
withRefreshToken
之后你可以这样做:
export default withRefreshToken(HomeScreen);
【讨论】:
以上是关于如何使用 apollo-hooks 为 react-native 创建实用程序的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Typescript 为 React 设置 Material-UI?
如何使用 React、Jest 和 React-testing-library 为带有令牌的 api 调用编写单元测试?
如何使用 Firebase 托管为 React SPA 提供 404 页面?
如何使用 createBottomTabNavigator 为 React Navigation 过渡设置动画?