【问题标题】:How to dynamically update Apollo Client (headers) using MobX如何使用 MobX 动态更新 Apollo 客户端(标头)
【发布时间】: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


【解决方案1】:

我希望这个答案对将来想在 MobX 中使用 Apollo 客户端的人有所帮助。经过一些实验,我得出结论,存储@client 的最佳方式是使用可观察变量。在我的例子中,我创建了两个可观察变量 - @client 和令牌,以及一个更新令牌的操作,当它运行时,它调用该操作来更新客户端并将一个新令牌传递给这个(客户端更新)操作。在所有其他商店中,必须知道@client 的状态,在类构造函数中,我创建了一个使用@client 作为谓词的反应和一个更新此商店的操作作为反应

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-31
    • 2018-02-14
    • 1970-01-01
    • 2020-09-29
    • 2018-12-19
    • 2021-03-11
    • 2018-11-04
    • 2021-09-16
    相关资源
    最近更新 更多