perf: optimize load of pagination items with caching

This commit is contained in:
Divlo
2022-08-24 17:22:55 +02:00
parent 19fc29ad47
commit ad64f1c571
16 changed files with 168 additions and 65 deletions

View File

@ -9,27 +9,36 @@ import { fetchRefreshToken } from './authenticationFromServerSide'
export class Authentication {
public tokens: Tokens
public accessTokenAge: number
public socket: Socket
public socket?: Socket
public api: AxiosInstance
constructor(tokens: Tokens) {
constructor(tokens: Tokens, disableSocketIO: boolean = false) {
this.tokens = tokens
this.accessTokenAge = Date.now()
this.socket = io(API_URL, {
auth: { token: `Bearer ${tokens.accessToken}` }
})
this.socket.on('connect_error', (error) => {
if (error.message.startsWith('Unauthorized')) {
fetchRefreshToken(this.tokens.refreshToken)
.then(({ accessToken }) => {
this.setAccessToken(accessToken)
})
.catch(async () => {
this.signout()
return await Promise.reject(error)
})
}
})
if (disableSocketIO || typeof window === 'undefined') {
this.socket = undefined
} else {
this.socket = io(API_URL, {
auth: { token: `Bearer ${tokens.accessToken}` }
})
this.socket.on('connect', () => {
console.log(
`Connected to socket with clientId: ${this.socket?.id ?? 'undefined'}`
)
})
this.socket.on('connect_error', (error) => {
if (error.message.startsWith('Unauthorized')) {
fetchRefreshToken(this.tokens.refreshToken)
.then(({ accessToken }) => {
this.setAccessToken(accessToken)
})
.catch(async () => {
this.signout()
return await Promise.reject(error)
})
}
})
}
this.api = axios.create({
baseURL: API_URL,
headers: {
@ -83,13 +92,14 @@ export class Authentication {
this.tokens.accessToken = accessToken
this.accessTokenAge = Date.now()
const token = `${this.tokens.type} ${this.tokens.accessToken}`
if (typeof this.socket.auth !== 'function') {
if (typeof this?.socket?.auth !== 'function' && this.socket != null) {
this.socket.auth.token = token
}
}
public signout(): void {
cookies.remove('refreshToken')
window.localStorage.clear()
window.location.href = '/authentication/signin'
}

View File

@ -25,7 +25,8 @@ export const AuthenticationProvider: React.FC<
const [user, setUser] = useState<UserCurrent>(props.authentication.user)
const authentication = useMemo(() => {
return new Authentication(props.authentication.tokens)
const disableSocketIO = typeof window === 'undefined'
return new Authentication(props.authentication.tokens, disableSocketIO)
// eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this memo once
}, [])
@ -33,7 +34,9 @@ export const AuthenticationProvider: React.FC<
useEffect(() => {
setLanguage(props.authentication.user.settings.language).catch(() => {})
setTheme(props.authentication.user.settings.theme)
return () => {
authentication?.socket?.disconnect()
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this effect once
}, [])

View File

@ -71,7 +71,7 @@ export const authenticationFromServerSide = (
} else {
try {
let data: any = {}
const authentication = new Authentication(tokens)
const authentication = new Authentication(tokens, true)
const { data: currentUser } = await authentication.api.get<
unknown,
AxiosResponse<UserCurrent>

34
tools/cache.ts Normal file
View File

@ -0,0 +1,34 @@
import { PaginationItem } from '../hooks/usePagination'
export const GUILDS_CACHE_KEY = 'guilds' as const
export const CHANNELS_CACHE_KEY = 'channels' as const
export const MEMBERS_CACHE_KEY = 'members' as const
export const MESSAGES_CACHE_KEY = 'messages' as const
export type CacheKey =
| typeof GUILDS_CACHE_KEY
| `${number}-${typeof CHANNELS_CACHE_KEY}`
| `${number}-${typeof MEMBERS_CACHE_KEY}`
| `${number}-${typeof MESSAGES_CACHE_KEY}`
export const getPaginationCache = <T extends PaginationItem>(
key: CacheKey
): T[] => {
const cache = localStorage.getItem(key)
if (cache != null) {
try {
const data = JSON.parse(cache)
if (Array.isArray(data)) {
return data
}
} catch {}
}
return []
}
export const savePaginationCache = <T extends PaginationItem>(
key: CacheKey,
data: T[]
): void => {
localStorage.setItem(key, JSON.stringify(data))
}

View File

@ -1,4 +1,5 @@
import { SetItems } from '../hooks/usePagination'
import { CacheKey, savePaginationCache } from './cache'
export interface Item {
id: number
@ -13,6 +14,7 @@ export interface SocketData<T extends Item = Item> {
export interface HandleSocketDataOptions<T extends Item = Item> {
setItems: SetItems<T>
data: SocketData<T>
cacheKey?: CacheKey
}
export type SocketListener = (data: SocketData) => void
@ -20,7 +22,8 @@ export type SocketListener = (data: SocketData) => void
export const handleSocketData = <T extends Item = Item>(
options: HandleSocketDataOptions<T>
): void => {
const { data, setItems } = options
const { data, setItems, cacheKey } = options
console.log('socket.io data received: ', data)
setItems((oldItems) => {
const newItems = [...oldItems]
@ -47,6 +50,9 @@ export const handleSocketData = <T extends Item = Item>(
break
}
}
if (cacheKey != null) {
savePaginationCache(cacheKey, newItems)
}
return newItems
})
}