|
| 1 | +--- |
| 2 | +title: FAQ |
| 3 | +order: 1 |
| 4 | +toc: menu |
| 5 | +nav: |
| 6 | + title: FAQ |
| 7 | + path: /faq |
| 8 | + order: 4 |
| 9 | +--- |
| 10 | + |
| 11 | +## 概览 |
| 12 | + |
| 13 | +用于记录一些项目中不常见的坑和解决办法。 |
| 14 | + |
| 15 | +## rxjs 和 hooks 使用陷阱 |
| 16 | + |
| 17 | +部分 UI 组件内部使用 rxjs 导致父组件回调函数中获取不到最新的状态 |
| 18 | +如 InputSelect 组件内部在 useEffect 内部使用 rxjs 绑定事件,当选择某项的时候,调用 onValueChange 传递选项给父组件,如果父组件在 onValueChange 回调中拷贝之前的 state 进行修改,然后更新 state,可能在回调中获取不到最新的 state。 |
| 19 | + |
| 20 | +```ts |
| 21 | +import { useState } from 'react'; |
| 22 | +import { times, cloneDeep, map } from 'lodash'; |
| 23 | +import { headings } from '../../texts/utils'; |
| 24 | +import { Stack } from '../../layouts/Stack'; |
| 25 | +import { roundedEm, theme } from '../../core/theme'; |
| 26 | +import { select } from '../../core/select'; |
| 27 | +import { Input } from '../Input'; |
| 28 | +import { InputSelect } from '../InputSelect'; |
| 29 | + |
| 30 | +export const InputSelects = () => { |
| 31 | + const [list, setList] = useState<Array<{ name: string }>>([]); |
| 32 | + |
| 33 | + const enums = ['Apple', 'Orange', 'Banana']; |
| 34 | + |
| 35 | + const handleChange = (val: string, index: number) => { |
| 36 | + const newVal = cloneDeep(list); |
| 37 | + newVal[index].name = val; |
| 38 | + setList(newVal); |
| 39 | + }; |
| 40 | + |
| 41 | + return ( |
| 42 | + <> |
| 43 | + {map(list, (val, index) => { |
| 44 | + return ( |
| 45 | + <Input> |
| 46 | + <InputSelect |
| 47 | + allowClear |
| 48 | + enum={enums} |
| 49 | + value={val} |
| 50 | + onValueChange={val => handleChange(val, index)} |
| 51 | + /> |
| 52 | + </Input> |
| 53 | + ); |
| 54 | + })} |
| 55 | + </> |
| 56 | + ); |
| 57 | +}; |
| 58 | +``` |
| 59 | + |
| 60 | +原因:由于 InputSelect 组件内部的 rxjs 事件监听函数只在组件挂载后执行一次,而 onValueChange 回调依赖了外部状态,外部状态的更新导致回调函数更新,而 InputSelect 内部的 onValueChange 不是最新的函数,故出现了 state 渲染出错的问题。 |
| 61 | + |
| 62 | +解决: |
| 63 | +setState 采用回调函数来获取最新的 state。 |
| 64 | + |
| 65 | +```ts |
| 66 | +const handleChange = (val: string, index: number) => { |
| 67 | + setList(prev => { |
| 68 | + const newVal = cloneDeep(prev); |
| 69 | + newVal[index].name = val; |
| 70 | + return newVal; |
| 71 | + }); |
| 72 | +}; |
| 73 | +``` |
| 74 | + |
| 75 | +## 自定义 hooks 的使用陷阱 |
| 76 | + |
| 77 | +部分自定义 hooks 使用不当导致页面崩溃 |
| 78 | +之前遇到过一次,开发环境页面正常,生产环境页面崩溃出现白屏, |
| 79 | +控制台报错指向如下链接:https://reactjs.org/docs/error-decoder.html/?invariant=300 |
| 80 | +大概意思是 hooks 执行顺序不对, |
| 81 | +最终定位到出错的组件大概逻辑如下: |
| 82 | + |
| 83 | +```ts |
| 84 | +import { useObservable } from '@reactorx/core'; |
| 85 | + |
| 86 | +const Example = () => { |
| 87 | + const { projectID, appName, env } = useInstance(); |
| 88 | + const [data, , requesting$] = useTempDataOfRequest( |
| 89 | + listEconomyIndex, |
| 90 | + { |
| 91 | + projectID, |
| 92 | + appIDOrName: appName, |
| 93 | + env, |
| 94 | + dataType: time?.timeType as IDataReportTimeTypeTimeType, |
| 95 | + dataTime: time ? from : undefined, |
| 96 | + }, |
| 97 | + [projectID, time], |
| 98 | + ); |
| 99 | + |
| 100 | + const requesting1 = useObservable(requesting$); |
| 101 | + |
| 102 | + const [data2, , requesting$2] = useTempDataOfRequest( |
| 103 | + listEconomyIndex, |
| 104 | + { |
| 105 | + projectID, |
| 106 | + appIDOrName: appName, |
| 107 | + env, |
| 108 | + dataType: time?.timeType as IDataReportTimeTypeTimeType, |
| 109 | + dataTime: time ? from : undefined, |
| 110 | + }, |
| 111 | + [projectID, time], |
| 112 | + ); |
| 113 | + |
| 114 | + const requesting2 = useObservable(requesting$2); |
| 115 | + |
| 116 | + if (requesting1 || requesting2) { |
| 117 | + return <div>Loading</div>; |
| 118 | + } |
| 119 | +}; |
| 120 | +``` |
| 121 | + |
| 122 | +以上组件出问题的 hook 是 useObservable,可以看到上面组件实现的功能是对两个请求的 loading 状态做了一个或运算,而 Webapck 在 prod 模式下启用编译压缩会将该或运算优化为如下代码: |
| 123 | + |
| 124 | +```ts |
| 125 | +if (useObservable(requesting$) || useObservable(requesting$2)) { |
| 126 | + return <div>Loading</div>; |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +由于或运算的短路特点,就会出现两个 hook 可能只执行一个的情况,生产环境下该页面就出现崩溃的现象。 |
| 131 | + |
| 132 | +解决: |
| 133 | + |
| 134 | +```ts |
| 135 | +const loading1 = useObservable(requesting$); |
| 136 | +if (loading1) { |
| 137 | + return <div>Loading</div>; |
| 138 | +} |
| 139 | + |
| 140 | +const loading2 = useObservable(requesting$2); |
| 141 | +if (loading2) { |
| 142 | + return <div>Loading</div>; |
| 143 | +} |
| 144 | +``` |
0 commit comments