# client-platform-react18
**Repository Path**: pleaseanswer/client-platform-react18
## Basic Information
- **Project Name**: client-platform-react18
- **Description**: 用react18相关内容实现客服链接-需求实战
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2024-04-22
- **Last Updated**: 2024-10-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### router
1. 自动化引入页面
- `import.meta.glob('pattern'[, options])` 提供了一种动态导入多个模块的方式。默认懒加载
- `pattern` 路径模式:用于匹配需要导入的模块
- `options`
- `eager: true` 确保所有组件默认属性为 `default`
- `import: 'default'` 指定要导入的组件默认属性为 `default`
2. 组件懒加载
```tsx
// LazyLoad.tsx
/**
* 组件懒加载,结合 Suspense 实现
* @param Component 组件对象
* @returns 返回新数组
*/
export const lazyLoad = (Component: React.LazyExoticComponent<() => JSX.Element>): React.ReactNode => {
return (
loading}>
);
};
// router.tsx
import { createBrowserRouter } from 'react-router-dom';
const router = {
// ...
element: lazyLoad(React.lazy(pageComs[compPath]))
};
export default createBrowserRouter(router);
// App.tsx
function App() {
return ;
}
```
### zustand
1. 创建 store
```ts
interface UserStore {
users: UserResp[];
selectIds: SelectIds;
updateSelectedId: (key: keyof SelectIdKey, value: number) => void;
fetchUsers: (params: UserRequest) => void;
}
export const useUserStore = create((set, get) => ({
users: cacheUsers,
selectIds: initSelectIds,
updateSelectedId: (key: keyof SelectIdKey, value: number) => {
set(state => ({
...state,
selectIds: {
...state.selectIds,
[key]: value
}
}));
},
fetchUsers: async (params: UserRequest) => {
if (get().users.length) return;
const { data } = await getUsers(params);
set({ users: data.data_list });
}
}));
```
2. 调用 store 中的数据|方法
```tsx
// Chat.tsx
export default function Chat() {
const { fetchUsers } = useUserStore();
useEffect(() => {
// ... params
async function fetchData() {
await fetchUsers(params);
}
let ignore = false;
if (!ignore) {
fetchData();
}
return () => {
ignore = true;
};
}, []);
}
```
```tsx
// Login.tsx
export default const Login = memo(function Login(props: Props) {
const { updateSelectedId } = useUserStore();
// 选择区服
const handleChangeServerId = useCallback(val => {
updateSelectedId('server_id', val);
});
// 选择角色
const handleChangePlayerId = useCallback(val => {
updateSelectedId('player_id', val);
});
})
```
### Question
#### 1. useEffect依赖项变化时并没有执行
##### 源码
```ts
useEffect(() => {
const selectedComp = selects.find(select => select.value === selected);
selectedComp && setSelectedComp(selectedComp);
}, [selected, selects, selectedComp]);
```
##### 分析
- 从提供的代码片段来看,这一行代码本身的逻辑是在查找 `selects` 数组中 `value` 属性等于 `selected` 的元素,并将这个元素赋值给状态 `selectedComp`
- 这段代码可能出现问题的原因主要可能包括以下几点:
1. 查找结果为 `undefined`:
- 如果 `selects` 数组中没有任何一个元素的 `value` 属性与 `selected` 相等,`find` 方法将返回 `undefined` 。这可能不是你的预期行为,尤其是如果你后续的代码依赖于 `selectedComp` 总是一个有效对象的话。确保你的代码能够妥善处理这种情况:
```ts
const selectedComp = selects.find(select => select.value === selected);
if (selectedComp) {
setSelectedComp(selectedComp);
} else {
// 处理未找到匹配项的情况,例如设置一个默认值或更新状态以反映未找到
}
```
2. 状态更新异步性
- `setSelectedComp` 是一个异步操作,这意味着状态的更新不会立即反映。如果你在调用 `setSelectedComp` 之后立即依赖于 `selectedComp` 的新值,可能会遇到问题。React 状态更新可能会被批处理,且实际更新的时机由 React 控制。正确的做法是在另一个 `useEffect` 中,依赖于 `selectedComp` 来执行需要该状态更新后的操作:
```ts
useEffect(() => {
//依赖于 selectedComp 的逻辑
}, [selectedComp]);
```
3. 检查是否有错误导致中断
- 如果 `useEffect` 中的代码抛出了错误,可能会导致后续代码不执行。尽管代码应该在错误之前执行,但值得确认是否有其他代码抛出了错误,特别是如果 `selectedComp` 是 `undefined` (即没有找到配的 select ),这可能会导致问题。你可以添加 `trycatch` 块来捕获并查看是否有错误发生:
```ts
useEffect(() => {
try {
const selectedComp = selects.find(select => select.value === selected);
if (!selectedComp) {
console.error("未找到匹配的 select");
return;
}
setSelectedComp(selectedComp);
} catch(error) {
console.error("useEffect 错误:",error);
}
}, [selected, selects])
```
#### 2. 函数获取不到最新的状态值
##### 源码
```ts
// 获取答案
const handleGetAnswer = (item: AnswerFields) => {
const { question, content, faqs } = item;
// 答案写入 todo...
setInputValue(question);
handleSendText();
};
// 发送信息 且机器人回复处理
const handleSendText = async () => {
if (!inputValue) return;
// ...
};
```
##### 分析
- 此时 `handleSendText` 函数调用时,期望获得最新的 `inputValue` 状态值,但此时遇到问题:在 `handleSendText` 里获取不到最新的 `inputValue` 值,这是因为状态更新时异步的,而且在 `set` 之后立即调用的函数内使用的是旧值
##### 解决方案
- 在 `useEffect` 中调用 `handleSendText` 方法
##### 实现方式
1. 需要增设状态值 `shouldSendText` 来标记是否需要发送文本
2. 用户回车且 `inputValue` 有效时,`setShouldSendText(true)`
3. 使用 `useEffect` 监听 `shouldSendText`,若为 `true` 则调用 `handleSendText` 且设置 `setShouleSendText(false)`
```ts
// 获取答案
const handleGetAnswer = (item: AnswerFields) => {
const { question, content, faqs } = item;
// 答案写入 todo...
setInputValue(question);
setShouldSendText(true);
};
// 用户回车发送消息
const handleKeyDown = event => {
if (event.key === 'Enter') {
setShouldSendText(true);
}
};
// 发送信息 且机器人回复处理
const handleSendText = async () => {
setShouldSendText(false);
if (!inputValue) return;
// ...
};
useEffect(() => {
shouldSendText && handleSendText();
}, [shouldSendText]);
```