【问题标题】:React - Error handling useState(). How to reset errors?React - 错误处理 useState()。如何重置错误?
【发布时间】:2022-01-03 08:26:08
【问题描述】:

简介

我正在处理我的应用程序中的所有错误。我注意到 React 可以处理不同类型的错误...

  1. 渲染错误(未定义的道具,...),可以使用 Error Boundary Component 和自定义后备组件来处理。

  2. 来自后端的错误,我想用 i18n.js 在 toast 中显示以翻译消息。

在 React 应用程序中结合这两种类型的错误处理是否常见?

在 toast 中显示错误

我决定创建自己的React Context,它会为通知、错误等呈现toast。这是我的实现:

/* eslint-disable react/prop-types */
import React, {
  createContext,
  useReducer,
  useMemo,
} from "react";

import toastReducer, {
  initialState,
  actionCreators,
} from "./helpers/reducers/toastReducer";
import { TOAST_TYPES, DEFAULT_TOAST_DURATION } from "../../utils/toast";
import Toast from "../../components/Toast";

const ToastContext = createContext(null);

export default ToastContext;

export function ToastProvider({ children }) {
  const [toast, dispatch] = useReducer(toastReducer, initialState);

  const open = (
    type = TOAST_TYPES[0],
    message,
    duration = DEFAULT_TOAST_DURATION,
    action = undefined
  ) => {
    dispatch(actionCreators.open(type, message, duration, action));
  };

  const close = () => {
    dispatch(actionCreators.close());
  };

  const value = useMemo(() => ({
    open,
    close,
  }), []);

  return (
    <ToastContext.Provider value={value}>
      {children}
      <Toast
        visible={toast.visible}
        type={toast.type}
        duration={toast.duration}
        action={toast.action}
        onDismiss={close}
      >
        {toast.message}
      </Toast>
    </ToastContext.Provider>
  );
}

我还实现了使用此上下文的 useToast() 钩子。

从服务器获取数据

当我使用钩子时,我在每个从我的数据库中获取数据的自定义钩子上将错误保存为“错误”状态。

// Based on the library "useQuery"
const useFetchSomeData = ({ fetchOnMount = true } = {}) => {
   const [data, setData] = useState([]);
   const [loading, setLoading] = useState(true);
   const [error, setError] = useState(null);   

   const cursor = useRef(new Date());
   const isFetching = useRef(false);

   const fetchData = async () => {
      if (isFetching.current) return;

      isFetching.current = true;

      setLoading(true);

      try {
         const data = await api.fetchData(cursor.current);
      } catch(err) {
         setError(err);
      } finally {
         setLoading(false);

         isFetching.current = false;
      }
   }

   useEffect(() => {
     if (fetchOnMount) {
        fetchData();
     }
   }, []);

   return {
      data,
      loading,
      error,
      fetchData
   }
}

在我的组件中我只是这样做:

const { data, error, loading } = useFetchData();

const toast = useToast();

const { t } = useI18N();

useEffect(() => {
  if (error) {
     toast.open("error", t(err.code));
  }
}, [error, t]);

问题

  1. 如您所见,在我的组件中,我只是使用useEffect 将翻译后的错误发送到我的 toast 上下文。但是......如果我从服务器两次收到相同的错误响应怎么办?我的意思是,错误没有在自定义钩子中重置为null...我该怎么做?

  2. 这种实现是否健壮(在您看来)并且在这些情况下很典型?

【问题讨论】:

  • 当我得到一个对象作为响应时,如果我只是跳过自定义钩子中的错误重置不会有问题......因为对象引用本身会有所不同(不是内容),但是想象一下错误的类型是一个字符串......那么我的组件的useEffect将不会被重新执行(因为dep没有改变)。

标签: reactjs react-native error-handling react-hooks try-catch


【解决方案1】:

老实说,我有点困惑,我想我理解你想要做什么,但不必这么复杂。

您应该使用ToastContainerToastContext(无论您想怎么称呼它)来包装您的整个应用程序,并在需要时导入您的toast,然后调用Toat.show("Error: ...")。查看库react-toastify 了解更多详细信息,也许这个库可以帮助您。您在应用入门级封装您的项目,再也不用担心它了。

就错误处理而言,在创建获取数据的函数时,您应该预期一次会出现一个错误并适当地处理它。我个人认为最好将错误返回给调用 fetch 函数的原始组件并在那里处理。在我看来,以这种方式跟踪事情更容易,但你所做的并没有错。

错误处理需要时间和提前思考,我不会否认这一点,但它会在构建糟糕的应用程序和经过深思熟虑的应用程序之间产生很大的不同。

我也不完全确定你为什么使用const isFetching = useRef(false); 而你可以使用const [isFetching, setIsFecthing] = useState(false),这就是useState 的用途。 useMemo 也一样。我认为这里的事情可能会更精简,因为您的应用程序会变得越来越复杂并且更容易维护。

【讨论】:

  • useRef 只是一个互斥变量。是的,我已经将错误返回给父级,然后在一个钩子“useErrorHandler”中处理它们,以便有一个集中的错误处理“控制器”。
猜你喜欢
  • 1970-01-01
  • 2023-01-29
  • 2020-07-20
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
相关资源
最近更新 更多