mirror of https://github.com/msgbyte/tailchat
feat: 切换频道时记录最后一次切换到的频道。减少无意义的操作
parent
944d7a7c72
commit
5e00ed8e25
@ -0,0 +1,26 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
import type { useEffect, useLayoutEffect } from 'react';
|
||||||
|
|
||||||
|
// Reference: https://github.com/alibaba/hooks/blob/master/packages/hooks/src/createUpdateEffect/index.ts
|
||||||
|
|
||||||
|
type EffectHookType = typeof useEffect | typeof useLayoutEffect;
|
||||||
|
|
||||||
|
export const createUpdateEffect: (hook: EffectHookType) => EffectHookType =
|
||||||
|
(hook) => (effect, deps) => {
|
||||||
|
const isMounted = useRef(false);
|
||||||
|
|
||||||
|
// for react-refresh
|
||||||
|
hook(() => {
|
||||||
|
return () => {
|
||||||
|
isMounted.current = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
hook(() => {
|
||||||
|
if (!isMounted.current) {
|
||||||
|
isMounted.current = true;
|
||||||
|
} else {
|
||||||
|
return effect();
|
||||||
|
}
|
||||||
|
}, deps);
|
||||||
|
};
|
@ -0,0 +1,85 @@
|
|||||||
|
/* eslint-disable no-empty */
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useMemoizedFn } from '../useMemoizedFn';
|
||||||
|
import { useUpdateEffect } from '../useUpdateEffect';
|
||||||
|
import _isFunction from 'lodash/isFunction';
|
||||||
|
import _isUndefined from 'lodash/isUndefined';
|
||||||
|
|
||||||
|
export interface IFuncUpdater<T> {
|
||||||
|
(previousState?: T): T;
|
||||||
|
}
|
||||||
|
export interface IFuncStorage {
|
||||||
|
(): Storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options<T> {
|
||||||
|
serializer?: (value: T) => string;
|
||||||
|
deserializer?: (value: string) => T;
|
||||||
|
defaultValue?: T | IFuncUpdater<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUseStorageState(getStorage: () => Storage | undefined) {
|
||||||
|
function useStorageState<T>(key: string, options?: Options<T>) {
|
||||||
|
let storage: Storage | undefined;
|
||||||
|
|
||||||
|
// https://github.com/alibaba/hooks/issues/800
|
||||||
|
try {
|
||||||
|
storage = getStorage();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializer = (value: T) => {
|
||||||
|
if (options?.serializer) {
|
||||||
|
return options?.serializer(value);
|
||||||
|
}
|
||||||
|
return JSON.stringify(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deserializer = (value: string) => {
|
||||||
|
if (options?.deserializer) {
|
||||||
|
return options?.deserializer(value);
|
||||||
|
}
|
||||||
|
return JSON.parse(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStoredValue() {
|
||||||
|
try {
|
||||||
|
const raw = storage?.getItem(key);
|
||||||
|
if (raw) {
|
||||||
|
return deserializer(raw);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (_isFunction(options?.defaultValue)) {
|
||||||
|
return options?.defaultValue();
|
||||||
|
}
|
||||||
|
return options?.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [state, setState] = useState<T>(() => getStoredValue());
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
setState(getStoredValue());
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
|
const updateState = (value: T | IFuncUpdater<T>) => {
|
||||||
|
const currentState = _isFunction(value) ? value(state) : value;
|
||||||
|
setState(currentState);
|
||||||
|
|
||||||
|
if (_isUndefined(currentState)) {
|
||||||
|
storage?.removeItem(key);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
storage?.setItem(key, serializer(currentState));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return [state, useMemoizedFn(updateState)] as const;
|
||||||
|
}
|
||||||
|
return useStorageState;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import { useMemo, useRef } from 'react';
|
||||||
|
import _isFunction from 'lodash/isFunction';
|
||||||
|
|
||||||
|
// From https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useMemoizedFn/index.ts
|
||||||
|
|
||||||
|
type Noop = (this: any, ...args: any[]) => any;
|
||||||
|
|
||||||
|
type PickFunction<T extends Noop> = (
|
||||||
|
this: ThisParameterType<T>,
|
||||||
|
...args: Parameters<T>
|
||||||
|
) => ReturnType<T>;
|
||||||
|
|
||||||
|
export function useMemoizedFn<T extends Noop>(fn: T) {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
if (!_isFunction(fn)) {
|
||||||
|
console.error(
|
||||||
|
`useMemoizedFn expected parameter is a function, got ${typeof fn}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fnRef = useRef<T>(fn);
|
||||||
|
|
||||||
|
// why not write `fnRef.current = fn`?
|
||||||
|
// https://github.com/alibaba/hooks/issues/728
|
||||||
|
fnRef.current = useMemo(() => fn, [fn]);
|
||||||
|
|
||||||
|
const memoizedFn = useRef<PickFunction<T>>();
|
||||||
|
if (!memoizedFn.current) {
|
||||||
|
memoizedFn.current = function (this, ...args) {
|
||||||
|
return fnRef.current.apply(this, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return memoizedFn.current as T;
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { createUpdateEffect } from './factory/createUpdateEffect';
|
||||||
|
|
||||||
|
export const useUpdateEffect = createUpdateEffect(useEffect);
|
@ -0,0 +1,3 @@
|
|||||||
|
import { createUseStorageState } from 'tailchat-shared';
|
||||||
|
|
||||||
|
export const useLocalStorageState = createUseStorageState(() => localStorage);
|
@ -0,0 +1,5 @@
|
|||||||
|
import { createUseStorageState } from 'tailchat-shared';
|
||||||
|
|
||||||
|
export const useSessionStorageState = createUseStorageState(
|
||||||
|
() => sessionStorage
|
||||||
|
);
|
@ -0,0 +1,30 @@
|
|||||||
|
import { useSessionStorageState } from './useSessionStorageState';
|
||||||
|
import { useMemoizedFn } from 'tailchat-shared';
|
||||||
|
|
||||||
|
interface UserSessionPerference {
|
||||||
|
/**
|
||||||
|
* 用户最后访问群组的面板id
|
||||||
|
* 用于切换群组时回到最后一个
|
||||||
|
*/
|
||||||
|
groupLastVisitPanel?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户偏好
|
||||||
|
* 用于在本地缓存一些不是那么重要的数据
|
||||||
|
*/
|
||||||
|
export function useUserSessionPreference<T extends keyof UserSessionPerference>(
|
||||||
|
scope: T
|
||||||
|
): [UserSessionPerference[T], (value: UserSessionPerference[T]) => void] {
|
||||||
|
const [preference = {}, setPreference] =
|
||||||
|
useSessionStorageState<UserSessionPerference>('sessionPreference');
|
||||||
|
const value = preference[scope];
|
||||||
|
const setValue = useMemoizedFn((value: UserSessionPerference[T]) => {
|
||||||
|
setPreference({
|
||||||
|
...preference,
|
||||||
|
[scope]: value,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return [value, setValue];
|
||||||
|
}
|
Loading…
Reference in New Issue