仅在 useEffect 之后使用数据

Posted

技术标签:

【中文标题】仅在 useEffect 之后使用数据【英文标题】:using the data only AFTER 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.. 【参考方案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))];

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

【讨论】:

感谢您的详细解释。出于我的目的,将数据初始化为空数组套装。但是,我接受您对详细解释的回答。谢谢。

以上是关于仅在 useEffect 之后使用数据的主要内容,如果未能解决你的问题,请参考以下文章

React 中带有 useEffect 的异步上下文

如何取消firebase的useEffect订阅

React:如何使用功能性 usestate/useEffect 复制特定的组件类 setstate 回调示例?

在材料ui标签中使用useEffect隐藏第三个标签

我希望 useEffect 仅在道具更改时运行。不在初始运行[重复]

React Hooks:确保 useEffect 仅在自定义钩子的数组参数内容更改时运行的惯用方法