【发布时间】:2021-09-20 13:38:12
【问题描述】:
我有一个客户端存储,如果令牌更改,则需要更新 @client(数据库驱动程序的类似物,但用于 Apollo 客户端)存储,关于用户的所有数据都存储在其中,当 @client 更改时,不会启动 UpdateUser 操作,或者启动但使用来自 ClientStorage 的旧 @client 版本
客户端存储:
import {action, computed, makeObservable, observable} from "mobx";
import {setContext} from "@apollo/client/link/context";
import {ApolloClient, ApolloLink, HttpLink, InMemoryCache} from "@apollo/client";
import {onError} from "apollo-link-error";
class Client{
token = localStorage.getItem('token'); //Токен авторизации, самая важная вешь в проекте!
// При запуски он достается из локального хранилища
changeToken(token){ //Функция для того, чтобы при логировании можно было записать новый токен
this.token = token
localStorage.setItem('token', token)
// console.log("CHANGE TOKEN")
}
constructor() {
makeObservable(this, {
token: observable,
changeToken: action,
AutoUpdatedApolloClient: computed({name: "UPDATE CLIENT"})
})
}
get AutoUpdatedApolloClient(){ //Если токен обновился, то эта вычисляемая функция обновляется и
//предоставляет всем элементам системы новый @client, например если пользователь залогинится,
//все последующие запросы и мутации будут происходить от его лица(в частности запрос на
// получение данных о пользователе), так же это позволит в будущем добавлять другие заголоки
//для запросов, если это понадобится
const authLink: any = setContext((_, { headers }) => {
// процесс создания авторизационного заголовка
return {
headers: {
...headers,
authorization: 'JWT '+ this.token,
}
}
});
//Ссылка на бэкенд
const httpLink = new HttpLink({
uri: '### SECRET URL ####'
// Additional options
});
const errorLink: any = onError(({ graphQLErrors }) => {
if (graphQLErrors) graphQLErrors.map(({ message }) => console.log(message))
})
//Конечная сборка @client
const client = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, httpLink]),
cache: new InMemoryCache()
});
console.log("new client")
//Новый клиент собран и расшеривается между всеми, кто его использует
return(client)
}
}
export default new Client
用户存储
import {autorun, makeAutoObservable, reaction} from "mobx"
import {GET_USER_DATA, LOGIN_MUTATION} from "./Struct";
import React from 'react';
import ClientStorage from "../ApolloStorage/ClientStorage";
class User{
username = ''//Имя пользователя, отображается в навигационной панели
mail = ''//Пока нигде не используется, может потом пригодится
isLogin = false //Вошел ли пользователь в систему или нет
userAccessLevel = "STUDENT"//Уровень доступа, если он будет ADMIN или TEACHER, то откроется редактор
doLoginSuccess = false //Результат того, смог ли пользователь залогинется в компоненте Login
doLoginReturnError = false //Нужна, чтобы понимать, что бы понимать, что при логине была получена ошибка
clientStorage = ClientStorage//Получаем прямой доступ и подписку на изменение в хранилище @client
//для Apollo (для Query и Mutation)
constructor() {
makeAutoObservable(this)
autorun(()=>this.UpdateUser())
reaction(()=>ClientStorage.AutoUpdatedApolloClient, ()=>this.UpdateUser())
}
doLogin(mail, password){
//Функция для того, чтобы можно было залогиниться, её основная задача в том,
//чтобы получить новый токен, он отправляется в ClientStorage, где собирается новый
//apollo client. Так же эта функция меняет isLogin, а это уже запускает авторан для
//выкачивания всех данных о пользователе(выкаивание производится уже на новом @client)
ClientStorage.AutoUpdatedApolloClient
.mutate({ mutation: LOGIN_MUTATION, variables:{
pass: password,
mail: mail
}})
.then( result=>{
try{
if(result.data.tokenAuth.success){
// console.log("User Store data ------")
// console.log(result)
ClientStorage.changeToken(result.data.tokenAuth.token)
this.isLogin = true
this.doLoginSuccess = true
this.doLoginReturnError = false
}else{
this.doLoginSuccess = false
this.doLoginReturnError = true
}
}
catch (e){
console.log(e)
}
})
}
doUnLogin(){
//Функция для выхода, при выходе сбрасывает вообще все переменные, связанные с пользователем
ClientStorage.changeToken('')//Уничтожаем старый токен
this.username = ''//Чтобы не осталось имя того, кто был залогинен до этого
this.mail = ''//Обнуляем не всякий случай
this.isLogin = false//Обновит навигацию и роуты
this.userAccessLevel = "STUDENT"//На всякий случай забирем право на использование редактора
this.doLoginSuccess = false //Обнуляем все переменные связанные с прозессом логина
this.doLoginReturnError = false //Обнуляем все переменные связанные с прозессом логина
ClientStorage.changeToken('')//Обновляем токен
}
UpdateUser() {
if(ClientStorage.AutoUpdatedApolloClient){//Важное изменение, необходимо для тригера на изменения
//в @client
//Функция, которая получает всю информацию о пользователе
// if(this.doLoginSuccess || this.isLogin || ClientStorage.token !==''){//Самая важная строчка
//благодаря ней мы тригиремся на любые изменения в токене, логине или ток, как проходит процесс
//логирования в систему
ClientStorage.AutoUpdatedApolloClient
.query({
query: GET_USER_DATA
})
.then(result => {
try{
this.username = result.data.me.username
this.userAccessLevel = result.data.me.userAccessLevel
this.isLogin = true
console.log("TRY TO UPDATE USER DATA")
console.log(result)
}
catch (e) {
console.log(e)
}
})
// }else{
// //На всякий случай при неудачном логирование обнуляем свойства пользователя
// this.isLogin = false
// this.username = ''
// this.userAccessLevel = "STUDENT"
// }
}
console.log("UPDATE USER DATA")
}
}
export default new User
【问题讨论】:
-
我已经用你的代码的某些部分做了一个简单的例子,它工作得很好。 codesandbox.io/s/… 一件事是您有非常奇怪的导出,例如
export default new Client没有实际的构造函数调用。我想这只是你的例子中的一个错字。此外,请不要在此处的问题中使用非英语语言,或使用ru.stackoverflow.com 网站解决此类问题 -
Danila,兄弟,感谢您注意到这个奇怪的类导出。我认为问题出在他身上,我是 MobX 的初学者,还不知道如何正确使用这个库。我在 En 语言段问了这个问题,因为我认为 MobX 在 Ru 段中使用的并不多,我要等很久才能得到答案。感谢您的帮助,很高兴我们来自同一个城市(圣彼得堡)。
标签: reactjs apollo-client mobx