javascript - React hooks - useEffect 详尽的 deps - 对 location.hash 的循环依赖
问题描述
我有一个useEffect
读取location.hash
并基于其他一些依赖项,将更改哈希。它看起来像这样:
useEffect(() => {
const hashAlreadyPresent = () => {
const hashArr = history.location.hash.split('#');
return hashArr.includes(hashId);
};
const addToHash = () => {
return history.location.hash.concat(`#${hashId}`);
};
const removeFromHash = () => {
const hashArray = history.location.hash.split('#').filter(hashStr => hashStr);
const indexOfHashId = hashArray.indexOf(hashId);
(indexOfHashId !== -1) && hashArray.splice(indexOfHashId, 1);
return hashArray;
};
// if hashId props is present then attach hash in route
hashId && !hashAlreadyPresent() && history.push({
hash: `${hashAlreadyPresent() ? '' : addToHash()}`,
search: history.location.search,
});
return () => {
// remove hashId only, retain any other hash if present
const hashArray = removeFromHash();
hashId && hashAlreadyPresent() && history.replace({
hash: hashArray.join('#'),
search: history.location.search,
});
};
}, [history, hashId, history.location.hash, history.location.search]);
history
来自 React Router 的位置。
逻辑是,一旦组件在屏幕上(已安装),它会向 URL 添加一个哈希,一旦它被卸载,它将从 url 中删除哈希。
当然,就 useEffect 而言,它转换为:如果任何依赖项发生变化,则先前的效果将被清除,并会生成一个新的效果实例。有效的 deps 规则帮助了我,因为之前我错过了这个钩子应该被清理并在hashId
更改时重新运行的事实。
现在,我们应该history.location.hash
对详尽的 deps 有依赖,但问题是每次我hash
从钩子内部更改时,钩子都会再次运行(前一个实例将清理并hash
再次更改),这将导致无限更新类型的情景。
注意:我知道这可以通过关闭穷举规则并排除history.location.hash
依赖项来实现,但想弄清楚重构/分解的任何可能性useEffect
,这样就可以在不关闭它的情况下解决这个问题。
需要注意的另一件事是,如果我添加history
为依赖项(我必须这样做,因为我使用的是 from 的方法history
),那么规则不会要求我显式添加嵌套的依赖项(history.lcoation.search
,history.location.hash
),但是应该添加那些,因为history
对象将保持不变,但嵌套对象会在 url 更改时更改。这与您将完整props
对象指定为依赖项而不是仅指定所需的特定嵌套属性的用例相同。
我是否应该根据位置更改在我的 useEffect 中有一个条件,这可以以某种方式告诉我位置是否从钩子内部更改,所以不做任何事情?
我是否应该以不同的方式解构和指定依赖项,以便在从效果中更改 location.hash 时效果不会运行?
注意: 在 github 上对此进行了讨论。得到了更多的见解。 https://github.com/facebook/react/issues/19636
解决方案
当指定非空依赖数组时,添加到依赖数组的任何值都会导致清理函数首先运行(第一次渲染除外),然后是效果函数(卸载期间除外)。要决定一个值是否应该进入依赖数组,请尝试为该值回答这个问题:
更新此值时,是否应再次运行效果,例如:
- 观察到预期的效果
- 如果需要,将清除之前所做的任何潜在更改
- 没有引入由于过时的引用导致的错误?
如果上述任何一点的答案是肯定的,那么该值将进入依赖数组。
我们现在可以为函数中使用的所有值回答上述问题useEffect
:
hashId
:是的。这是效果的主要驱动因素,每次此值更改时,URL 都应反映更改。这成为效果的真实来源。因此,这是确保观察到预期效果所必需的。此外,这也是清理 previous 所必需的,hashId
因为清理函数需要对 previous 的引用hashId
。history
:是的。我想由于这是由反应路由器提供的,因此引用不应在整个组件的生命周期中更改。从这个意义上说,在这里添加它的唯一目的是满足 lint 规则,没有实际影响(除了额外的引用检查)。但是,如果它确实发生了变化,效果函数将对它有一个过时的引用,这可能会导致错误。必须注意这一点。history.location.search
:没有。这与主要效果无关,因为只hashId
需要确保观察到所需的效果。也没有过时引用的危险,因为它总是从history
对象中读取。由于history
对象是可变的并且每次都使用最新值更新,并且已经是依赖数组的一部分,history.location.search
因此可以安全地省略。*history.location.hash
:不,对于与 for 相同的论点history.location.search
。此外,它始终hashId
决定history.location.hash
应该是什么,因此不应该使用对此值的更新来重新运行效果。
最终的依赖数组就是[hashId, history]
. **
* 注意不要从清理函数中提取search
和history.location
使用search
,因为这将是一个过时的引用
** 注意routeModal
在效果体中使用,如果需要,这也必须是依赖数组的一部分
推荐阅读
- git - 如何解决开发分支依赖
- java - 我需要创建一个进程来监视 selenium 执行,如果在执行期间到达,将单击弹出窗口
- javascript - 如何在加载所有 AngularJS 组件和元素结束时隐藏“加载”动画?
- r - 在 RStudio 帮助窗格中查找特定包的功能
- r - 使用 `$` 的部分列名。错误或功能?
- php - 循环遍历 ACF 组的所有行而不指定行名
- python - Pymongo 仅在传递空对象时返回 id 字段
- protractor - 端到端测试:-量角器错误:未知错误:调用函数结果缺少“值”
- android - 无法创建类 ViewModel kotlin 的实例
- java - 在 JUnit5 (Eclipse) 中创建 TestSuite