我有一个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]);
其中CCD_ 3来自React Router。
逻辑是,一旦组件在屏幕上(安装),它就会向URL添加一个哈希,一旦卸载,它就会从URL中删除该哈希。
当然,就useEffect而言,它意味着:如果任何依赖项发生变化,则会清除以前的效果,并创建新的效果实例。有效的deps规则帮助我做到了这一点,因为之前我忽略了这样一个事实,即如果hashId
发生变化,应该清理并重新运行这个钩子。
现在,对于穷举的deps,我们应该依赖history.location.hash
,但问题是,每次我从钩子内更改hash
时,钩子都会再次运行(上一个实例将清理并再次更改hash
),这将导致一种无限更新的情况。
注意:我知道关闭穷举的deps规则并从依赖项中排除history.location.hash
是可能的,但我想找出重构/分解useEffect
的任何可能性,这样就可以在不关闭它的情况下解决这个问题。
另一件需要注意的事情是,如果我添加history
作为依赖项(我必须添加,因为我使用的是history
中的方法),那么规则不会要求我显式添加嵌套依赖项(history.lcoation.search
、history.location.hash
),但应该添加这些依赖项,因为history
对象将保持不变,但嵌套对象将在url更改时更改。这与将完整的props
对象指定为依赖项而不是仅指定所需的特定嵌套属性的用例相同。
我是否应该在我的useEffect中有一个基于位置何时更改的条件,它可以以某种方式告诉我位置是否从钩子内部更改,所以什么都不做?
我是否应该以不同的方式销毁并指定依赖项,以便在效果中更改位置.hash时不会运行效果?
注意:在github上对此进行了讨论。得到了更多的见解。https://github.com/facebook/react/issues/19636
指定非空依赖数组时,添加到依赖数组中的任何值都会导致先运行清理函数(第一次渲染时除外),然后运行效果函数(卸载时除外)。要决定一个值是否应该进入依赖数组,请尝试回答该值的以下问题:
当更新此值时,是否应再次运行效果,例如:
- 观察到所需效果
- 如有需要,将清理之前所做的任何潜在更改
- 没有引入由于过时引用而导致的错误
如果上面任何一点的答案都是肯定的,那么该值将进入依赖数组。
我们现在可以回答上面关于useEffect
函数中使用的所有值的问题:
hashId
:是。这是效果的主要驱动因素,每次该值发生变化时,URL都应该反映出变化。这成为效果的真相来源。因此,这是确保观察到所需效果所必需的。此外,这也是清理先前hashId
所必需的,因为清理函数需要引用先前hashId
history
:是。我想,由于这是由react路由器提供的,所以在组件的整个生命周期中,引用不应该改变。从这个意义上说,在这里添加它的唯一目的是满足lint规则,而没有实际影响(除了额外的引用检查)。但是,如果确实发生了更改,则effect函数将有一个过时的引用,这可能会导致错误。这件事必须处理好history.location.search
:否。这与主要效果无关,因为只需要hashId
来确保观察到所需的效果。也没有过时引用的危险,因为它总是从history
对象中读取的。由于history
对象是可变的,每次都用最新的值更新,并且已经是依赖数组的一部分,因此可以安全地省略history.location.search
*history.location.hash
:否,用于与history.location.search
相同的参数。此外,始终是hashId
决定history.location.hash
应该是什么,因此不应使用对此值的更新来重新运行效果
那么最后的依赖数组就是history
0。**
*注意不要从history.location
中提取search
,并在清除函数中使用search
,因为它将是一个过时的引用
**注意到效果主体中使用了routeModal
,如果需要,它也必须是依赖数组的一部分