【问题标题】:using the data only AFTER useEffect仅在 useEffect 之后使用数据
【发布时间】:2022-01-18 13:51:58
【问题描述】:

我有一个反应组件(用 Typescript 编写),如下所示。

在这个组件中,我尝试读取包含数据的 excel,然后使用数据来渲染组件

由于在useEffect运行之前没有读取数据,所以menuData被初始化为null

读取数据工作正常,menuData 没有错误。

但是,let meals = [...new Set(menuData.map((item: menuItem) => item.Meal))]; 行抛出错误 TypeError: menuData is undefined。我认为这是因为在 menuData 仍为 undefined 时(在 useEffect 运行之前)执行了此行。

如何克服这个错误?也就是说,let meals = [...new Set(menuData.map((item: menuItem) => item.Meal))];let meals = [...new Set(menuData.map((item: menuItem) => item.Meal))];useEffect 运行后如何运行?

我想将此行保留在 useEffect 之外,因为需要对 menuData 对象数组执行更多的数组函数(map、filter...等)。

export const CategoryItems = ({ toggled }: CategoryItemsProps) => {
  
const [menuData, setMenuData] = useState<menuData | undefined>(undefined);

   /*Code to read the excel with menu items once the page is loaded.
  The sheet will be read only once*/
  useEffect(() => {
    let url = "data.xlsx";
........
....
.....
      setMenuData(XLSX.utils.sheet_to_json(worksheet, { raw: true }));

  }, []);

 let meals = [...new Set(menuData.map((item: menuItem) => item.Meal))];

  return (
    <div className={`category-items ${toggled ? "theme-dark" : "theme-light"}`}>
.
.
.
    //additional code to render the component based on the variable `meals`
.
.
.
      </div>
    </div>
  );
};

【问题讨论】:

  • 为什么不把状态初始化为一个空数组呢? const [menuData, setMenuData] = useState&lt;menuData&gt;([]);
  • 我假设你的 useEffect 是异步的,所以你不能让它在它之后运行,因为这是将来会发生的事情。但是你可以做的是在 setMenuData 将触发的第二个渲染上运行它。你可以让 menuData 最初为空,但我个人喜欢提前返回 null 选项,如果它不处于要渲染的状态,为什么还要费心处理 JSX。所以在你的let meals 之前,只要做if (!menuData) return null;,你当然可以用某种形式的加载指示器替换null,这取决于你的useEffect 需要多长时间,如果这是你做的事情。
  • @PedroFeltrin 这行得通!如果您想将此作为答案。我会投票赞成。
  • @Keith 我不确定我是否完全理解您的建议。我应该在哪里添加if (!menuData) return null?在useEffect内?
  • @moys 不直接放在你的let meals之前,基本上在使用useEffect的时候,你一般会做多次渲染,通常不需要第一次渲染,因为你在等待来自的数据useEffect..

标签: javascript use-effect


【解决方案1】:

只需将您的状态初始化为一个空数组即可。

const [menuData, setMenuData] = useState<menuData>([]);

应该可以!

【讨论】:

    【解决方案2】:

    如果数据尚未到来,我个人喜欢退出渲染阶段,因为useEffect 内的setState 无论如何都会导致另一个渲染。

    if (!menuData) return null;
    let meals = [...new Set(menuData.map((item: menuItem) => item.Meal))];
    

    null 只是一种不渲染的简单方法..

    我喜欢这种方式,如果您发现 useEffect 有时可能需要一些时间,而不是返回 null,我们也许可以返回某种形式的加载指示器..

    if (!menuData) return <div className="loading">Loading..</div>;
    let meals = [...new Set(menuData.map((item: menuItem) => item.Meal))];
    

    将数据初始化为空的东西是可行的,但是你有点松散了一些隐式状态,如果你渲染的结构更复杂,我发现提早退出更容易推理..

    【讨论】:

    • 感谢您的详细解释。出于我的目的,将数据初始化为空数组套装。但是,我接受您对详细解释的回答。谢谢。
    猜你喜欢
    • 2021-06-30
    • 2021-07-14
    • 2020-07-11
    • 2021-08-02
    • 2022-08-11
    • 2023-02-07
    • 1970-01-01
    • 2020-04-16
    • 2022-01-05
    相关资源
    最近更新 更多