这是避免“你在提前返回后不小心调用了 React Hook 吗?”的安全方法吗?

Posted

技术标签:

【中文标题】这是避免“你在提前返回后不小心调用了 React Hook 吗?”的安全方法吗?【英文标题】:Is this a safe way to avoid "Did you accidentally call a React Hook after an early return?" 【发布时间】:2019-09-23 06:00:31 【问题描述】:

我写了一个这样的函数组件:

export const SiteMap: React.FunctionComponent = () => 

  return useGetSetData<I.SiteMap>(
    title: "Site Map",
    getData: () => IO.getSiteMap(),
    showData: Page.SiteMap
  );

效果很好。

顺便说一下,我的useGetSetData 是一个“高阶组件”函数,它使用useStateuseEffect 来获取数据,然后将数据(在获取之后)传递给传入的演示文稿要显示的组件。 它的参数是:

interface UseGetDataPropsT<T> 
  title: string,
  getData: () => Promise<T>,
  showData: (data: T) => ReactElement

无论如何,下次我尝试使用一个页面,其内容取决于 URL 中的其他一些内容,我的代码如下:

type RouterProps = ReactRouter.RouteComponentProps<any>;

export const Image: React.FunctionComponent<RouterProps> = (props: RouterProps) => 

  const imageId: number | undefined = getId(props, "Image");
  if (!imageId) 
    return NoMatch(props);

  return useGetSetData<I.Image>(
    title: "Image",
    getData: () => IO.getImage(imageId),
    showData: Page.Image
  );

这会产生一条错误消息:

有条件地调用 React Hook "useGetSetData"。在每个组件渲染中,必须以完全相同的顺序调用 React Hooks。你是否在提前返回后不小心调用了 React Hook?反应钩子/钩子规则

如果我重新编码如下,那么它可以工作:

export const Image: React.FunctionComponent<RouterProps> = (props: RouterProps) => 

  const imageId: number | undefined = getId(props, "Image");
  if (!imageId) 
    return NoMatch(props);

  return ImageId(imageId);


export const ImageId: React.FunctionComponent<number> = (imageId: number) => 
  return useGetSetData<I.Image>(
    title: "Image",
    getData: () => IO.getImage(imageId),
    showData: Page.Image
  );

这是一个微不足道的更改,即它在功能上与我之前编写的代码相同。

它避免了上面的错误消息,并且看起来动作正确。


我的问题是:

我的变通方法安全吗,即这段代码可以吗? 如果不是那么在什么情况下会失败

【问题讨论】:

【参考方案1】:

不安全react-hooks/rules-of-hooks 只是一个 eslint rule,还不够聪明(还)意识到你被骗了

问题和之前一模一样,在https://overreacted.io/why-do-hooks-rely-on-call-order/中解释过

解决方案是有条件地渲染一个单独的组件(这将无条件地调用钩子)=> 使用 JSX 的 React.createElement(ImageId...) 而不是调用普通函数:

  if (!imageId) 
    return <NoMatch ...props />;
  

  return <ImageId ...imageId />;

【讨论】:

但是调用顺序总是一模一样的,不是吗?要么页面 URL 包含一个有效的“imageId”部分——在这种情况下,钩子和 get-and-show 总是被调用——或者 URL 是错误的,在这种情况下它们永远不会被调用。这怎么不安全? ImageId 组件与 ImageId 函数有何不同——它们不是等价的吗? 代码 if (!imageId) 不知道该信息来自不变的 URL - 在未来的版本中,URL 可能会更改而无需重新加载页面 使用 ImageId 作为组件会挂载一个新的子 React 元素(在开发模式下称为“ImageId”),而将 ImageId 作为函数调用只是使用返回值,就好像它是内联编写的没有中间元素(参见浏览器中的 React 开发工具) function component 是否与您所说的更安全的“组件”相同?如果是这样,“函数组件”和“函数”之间有什么区别(即 React 如何确定函数是否是组件)?

以上是关于这是避免“你在提前返回后不小心调用了 React Hook 吗?”的安全方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

FAST_FORWARD 游标何时会有工作表(这是要避免的)?

存储 NSUserDefaults (Xcode) 时避免重复

避免直接改变道具 - Datatable VueJS

如何动态添加struct属性以避免冗余代码? [关闭]

避免作曲家的autoload.php解析所有文件

如何避免 SQL 注入攻击?