How to dynamically update Apollo Client (headers) using MobX

Viewed 169

I have a Client Storage, it is needed to update @client (an analogue of the driver for the database, but used for the Apollo Client), if the token is changed There is a User Storage, all data about users is stored in it, when @client is changed, the UpdateUser action is not launched, or starts but uses the old @client version from ClientStorage

Client Storage:

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

User Storage

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
1 Answers

I hope this answer is helpful for anyone who wants to use the Apollo Client with MobX in the future. After some experimentation, I've come to the conclusion that the best way to store @client is with an observable variable. In my case, I created two observable variables - @client and token, and an action to update the token, when it runs, it calls the action to update the client and passes a new token to this(client update) action. In all other Stores, that must to know state of @client, in the class constructor I created a reaction that use @client as as predicate and an action to update this store as a reaction

Related