这是避免“你在提前返回后不小心调用了 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
是一个“高阶组件”函数,它使用useState
和useEffect
来获取数据,然后将数据(在获取之后)传递给传入的演示文稿要显示的组件。
它的参数是:
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 游标何时会有工作表(这是要避免的)?