用户中心实现 UserCenter + hooks#
考察:ts 基础 + react hooks + async/await + class + 解决业务问题能力
题面#
要实现一个 React 聊天页面,页面很多地方(聊天列表、chat room navbar、对话列表)等都需要用到用户信息展示的场景。用 ts 实现一个 UserCenter 的 class,他提供根据 user id 返回 user info 的方法。用户的数据通过 API 请求获取,API 接口支持 传入 id 列表。UserCenter 可以对用户数据 进行 统一获取、缓存和更新。
一些信息:#
// type schema
interface User {
id: string;
name: string;
avatar: string;
}
// API
const fetchUsers = async (ids: string[]): Promise<User[]> => [];
需要实现:#
// 1. UserCenter class:
// - 避免过度请求(避免重复请求同一 id 的数据,避免300ms内发起多个请求)
class UserCenter {
constructor() {}
async getUser(id: string): Promise<User> {}
// ...
}
// 2. react 组件内使用
// - const {user,loading} = useUser(id)
function useUser(id: string): {
user: User | null;
loading: boolean;
};
参考答案#
interface User {
id: string;
name: string;
avatar: string;
}
const fetchUsers = async (ids: string[]): Promise<User[]> => [];
class UserCenter {
userCache = new Map<string, User>();
waitList: string[] = [];
fetchingList: string[] = [];
callBacks: Map<string, Function[]> = new Map();
private async fetchList() {
if (this.waitList.length === 0) return;
this.fetchingList = [...this.fetchingList, ...this.waitList];
fetchUsers(this.waitList).then((users) => {
users.forEach((user) => {
this.userCache.set(user.id, user);
this.callBacks.get(user.id)?.forEach((cb) => cb(user));
this.callBacks.delete(user.id);
});
this.fetchingList = this.fetchingList.filter(
(id) => !this.userCache.has(id)
);
});
this.waitList = [];
}
private async fetchUser(id: string): Promise<User> {
if (!this.waitList.includes(id) && !this.fetchingList.includes(id))
this.waitList.push(id);
return new Promise((resolve) => {
this.callBacks.set(id, [...(this.callBacks.get(id) || []), resolve]);
});
}
constructor() {
setInterval(this.fetchList, 300);
}
async getUser(id: string) {
if (this.userCache.has(id)) return this.userCache.get(id);
return this.fetchUser(id);
}
}
const userCenter = new UserCenter();
const useUser = (
id: string
): {
user: User | null;
loading: boolean;
} => {
const [user, setUser] = useState<User>(null);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
setLoading(true);
userCenter.getUser(id).then((user) => {
setUser(user);
setLoading(false);
});
}, [id]);
return {
user,
loading,
};
};