По сути, у нас есть это в нашем файле index.js для настройки авторизации ApolloProvider для выполнения запросов / мутаций.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
let session = localStorage.getItem("session");
let authorization = "";
if (session) {
let sessionObj = JSON.parse(session);
authorization = sessionObj.access_token
}
const graphQLServerURL = process.env.REACT_APP_API_URL;
const client = new ApolloClient({
uri: graphQLServerURL + "/graphql",
headers: {
authorization,
}
});
ReactDOM.render(
<ApolloProvider client = {client}>
<App />
</ApolloProvider>
, document.getElementById('root'));
Когда приложение загружается впервые, заголовок authorization будет null. Однако внутри компонента <App> у нас есть компонент <Login>, который в основном выполняет почтовый запрос с username и password. При успешном запросе в методе .then() имеем:
.then(res => {
if (res === 200) {
localStorage.setItem("session", JSON.stringify(res.data));
history.push("/dashboard");
});
Итак, что происходит, пользователь перенаправляется на компонент <Dashboard>, у которого есть компонент <Query> (для вывода некоторых данных). Однако authorization в ApolloClient по-прежнему null, пока я не нажму кнопку «Обновить». Использование push не перезагружает компонент <App> (так что он получает обновленный session из локального хранилища).
Как мне сделать это таким образом, чтобы после успешного почтового запроса при входе в систему авторизация от index.js получала последний session объект без перезагрузки всего приложения?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вам придется использовать ссылку. Ссылка
const httpLink = createHttpLink({
uri: '/graphql',
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
});
Однако наше приложение использует ApolloClient из apollo-boost и не распознает вариант link.
Дэнг, хотелось бы, чтобы это не было заминусовано. С новым клиентом Apollo 3 это предпочтительный путь, который сэкономил бы мне время. Полностью пропустил этот ответ из-за отрицательных голосов >.>
@afreeland согласился, этот ответ сильно недооценен.
Вы можете использовать функцию request, если используете apollo-boost
const getToken = () => {
const token = localStorage.getItem('token');
return token ? `Bearer ${token}` : '';
};
const client = new ApolloClient({
uri: `${graphQLServerURL}/graphql`,
request: (operation) => {
operation.setContext({
headers: {
authorization: getToken(),
},
});
},
});
Если вы используете Apollo Client 3, вы должны проверить ответ ниже от ncabral, который использует setContext Apollo Client. Этот ответ упоминается в последних документах API клиента Apollo (см. ссылку REF в ответе ncabral).
У меня была аналогичная проблема. Я хотел использовать token в ApolloProvider. Когда приложение рендерилось в первый раз, я не мог получить доступ к localStorage, в результате WebSocket не мог работать.
Я создал собственный хук и использовал useEffect и useState для его обновления.
import {
ApolloClient, InMemoryCache, HttpLink, split,
} from '@apollo/client';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { useEffect, useState } from 'react';
import config from './index';
export const useConfigClient = () => {
const [authTokenStorage, setAuthTokenStorage] = useState(localStorage.getItem('token')); // by default it is null
const cache = new InMemoryCache();
const httpLink = new HttpLink({
uri: config.GRAPHQL_URL,
headers: {
authorization: `Bearer ${authTokenStorage}`,
},
});
const wsLink = new WebSocketLink({
uri: config.GRAPHQL_WS,
options: {
reconnect: true,
connectionParams: {
headers: {
authorization: `Bearer ${authTokenStorage}`,
},
},
},
});
const link = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
useEffect(() => {
setAuthTokenStorage(localStorage.getItem('token')); // as soon as it is available just update the token
}, []);
const client = new ApolloClient({
cache,
link,
});
return client;
};
import { ApolloProvider } from '@apollo/client';
import { useConfigClient } from '../config/apolloConfig'; // import the hooks
<ApolloProvider client = {useConfigClient()}>
// ... add your component
</ ApolloProvider>
Я не уверен, что это лучший способ исправить это. На данный момент это работает, и я надеюсь, что это поможет другим.
Когда я использую это, я получаю сообщение об ошибке Invalid Hook Error. «Хук можно вызывать только внутри тела функционального компонента». что имеет смысл, так как используемая здесь функция не является компонентом.
Другой способ добиться этого — использовать метод setLink из apollo-config.
После этого экспортируйте функцию, которая установит заголовки, как только у вас появится токен.
import {ApolloClient, InMemoryCache, HttpLink} from '@apollo/client';
import {setContext} from '@apollo/client/link/context';
const httpLink = new HttpLink({uri: '/graphql'});
let config = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
export function setGraphqlHeaders() {
const token = localStorage.getItem(AUTH_TOKEN);
const authLink = setContext((_, {headers}) => {
return {
headers: {
...headers,
authorization: token || null,
},
};
});
config.setLink(authLink.concat(httpLink));
}
Надеюсь, это поможет кому-то
Ответ praveen-me работает для меня в 2021 году с Nextjs. Я попробовал ответ ncabral, но не уверен, что он автоматически обновит JWT для меня.
В дополнение к ответу praveen-me я добавил кое-что в код для себя:
userToken.ts
const userTokenKey = 'userToken'
export const getUserToken = () => {
try {
return localStorage.getItem(userTokenKey)
} catch (error) {
...
}
}
export const setUserToken = (token: string) => {
try {
localStorage.setItem(userTokenKey, token)
} catch (error) {
...
}
}
apolloClient.ts
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { getUserToken } from "./userToken";
const httpLink = createHttpLink({
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL
})
let client = new ApolloClient({
// link: authLink.concat(httpLink),
link: httpLink,
cache: new InMemoryCache(),
});
export function updateGraphqlHeaders() {
const authLink = setContext((_, { headers }) => {
const token = getUserToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
})
client.setLink(authLink.concat(httpLink))
}
updateGraphqlHeaders() // Init
export default client;
_app.tsx
...
import { ApolloProvider } from "@apollo/client";
import client from "../auth/apolloClient";
function MyApp({ Component, pageProps }: AppProps) {
return (
<ApolloProvider client = {client}>
<Component {...pageProps} />
</ApolloProvider>
);
}
export default MyApp;
Похоже, что документы Apollo Client 3 показывают, как включать заголовок в каждый вызов. apollographql.com/docs/react/networking/authentication/#header ncabral ниже имеет ответ