# 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]); ```