React Custom Hooks-处理错误



我在toast中显示所有api请求错误。

在我的代码中,我分离了概念,将组件逻辑转移到business/ui挂钩。

为了呈现toast(一个命令组件(,我只在一个功能组件中执行以下操作:

const toast = useToast(); // UI hook
toast.display(message, { type: "error", duration: 500 });

而且,为了连接到我的api,我可以使用自定义的业务挂钩,例如:

const useRequestSomething() {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const isRequesting = useRef(false);
const requestSomething = async (someParam, onSuccess = undefined, onError = undefined) => {
if (isRequesting.current) return;
isRequesting.current = true;
setIsLoading(true);
try {
const data = await api.requestSomething(someParam);
setData(data);
onSuccess?.();
} catch(err) {
onError?.(err);
}
setIsLoading(false);
isRequesting.current = false;
}
return {
data,
isLoading,
requestSomething
}
}

我主要关心的是概念的分离。。。我认为在这个钩子中使用useToast((不是一个好主意,这个钩子是我的业务逻辑的容器。。。尽管这可能是个好主意。

因此,为了处理任何组件中的错误,我可以做一些类似的事情:

function MyComponent() {
const toast = useToast();
const { t } = useTranslation(); // i18n.js hook
const { data, isLoading, requestSomething } = useRequestSomething(); 
const handleOnPress = () => {
requestSomething("x", undefined, handleOnRequestSomethingError);
}
const handleOnRequestSomethingError = (err) => {
toast.display(t(err), { type: "error", duration: 500 });
}
... JSX
} 

我似乎已经用业务挂钩定义了某种基于回调的api。。。你觉得我的实现怎么样?

在钩子内部以这种方式(通过回调(处理错误是一种反模式吗?

处理这种情况的典型方法是什么?(由于我的后端原因,我无法使用useQuery(

我认为你的解决方案很好,但是,IMHO,我不想过早地处理错误,我喜欢让错误传播到我们真正知道如何处理它的地方。例如,我会这样做。

const requestSomething = async (params) = {
...
try {
await api.doRequest(params);
} catch (err) {
... do some common clean up ...
throw err;
}
}
const handleOnPress = async () => {
try {
await requestSomething("x");
} catch (err) {
toast.display(t(err), { type: "error", duration: 500 });
}

}

实际上,我会把它包装在一个像这样的通用错误处理程序中。

const handleOnPress = async () => {
await withGeneralErrorHandling(async () => {
try {
await requestSomething("x");
} catch (err) {
if (err.errorCode === 'SOME_KNOWN_CASE') {
toast.display(t(err), { type: "error", duration: 500 });
} else {
throw err;
}
}
});
}
async function withGeneralErrorHandling(callback: () => Promise<void>) {
try {
await callback()
} catch (err) {
if (err.errorCode === 'GENERAL_CASE1') { ...}
else if (err.errorCode === 'GENERAL_CASE2') { ... }
else {
if (isProduction) { reportError(err); }
else { throw err; }
}
}
}

这是因为我通常无法在第一次实现时列出所有错误案例。每个错误案例都将被逐步发现。我必须让它快速失效,让它传播到尽可能靠近最外面的控制器。

通过利用这种内置的错误传播,您可以保留堆栈跟踪信息,并可以准确地知道错误发生的位置。

是的,您的组件知道Toast,未来每个处理一些错误的组件都知道Toast

这使得错误处理逻辑有点僵化,如果将来需要使用另一种方法来处理错误,则必须编辑每个组件。我会使用一些状态管理系统(redux,mopx,随便什么(。其想法是,为了显示错误,您需要更新应用程序的状态。您的toast组件将订阅状态更改并做出相应的反应。

这种方式依赖于状态,而不是一些实际的组件/显示错误的方式,这更抽象、更灵活。

最新更新