useEffect 中的所有变量都绝对需要列为依赖项吗?

Are all variables inside a useEffect absolutely required to be listed as dependencies?

useEffect 中使用的所有变量是否始终、绝对、无一例外地需要指定为依赖项?

我的用例(为演示目的而简化)涉及根据浏览器 window 的宽度执行的不同功能,但当浏览器 window 更改时 运行 不执行:

const scrollToTop = () => window.scrollTo(0, 0)
const scrollToTopOfArticle = () => window.scrollTo(0, 200)

function App({
  isDesktop,
  selectedArticle
}) {

  useEffect(() => {
    isDesktop ? scrollToTop() : scrollToTopOfArticle()
  }, [selectedArticle])

  return (
    <div className="App">
      <h1>{selectedArticle.title}</h1>
      <p>{selectedArticle.body}</p>
    </div>
  );
}

如果我将 isDesktop 添加到依赖项中,那么只要用户在移动设备和桌面之间调整 window 的大小,就会产生 运行s 的效果,这是不希望的,但我也知道教条效果中使用的所有内容都必须列为依赖项。

关于如何协调这两个要求有什么建议吗?

Are all variables inside a useEffect absolutely required to be listed as dependencies?

是,否则会产生警告。

这就是为什么最好清楚每个效果应该做什么(即关注点分离)。

因此,您可以拥有两个具有不同依赖关系的独立效果。

类似于:

useEffect(
  () => {
    scrollToTopOfArticle();
  }, [selectedArticle]
);

useEffect(
  () => {
    if (selectedArticle && isDesktop) {
      scrollToTop();
    }
  }, [isDesktop, selectedArticle]
)

如果您想让 useEffect() 仅响应 selectedArticle 中的更改,请使用 isDesktopselectedArticle 来初始化组件状态。每当 selectedArticle 发生变化时,第一个 useEffect() 将使用传入的道具更新两个状态,并触发第二个 useEffect() 在下一次渲染时重新 运行。

const scrollToTop = () => window.scrollTo(0, 0)
const scrollToTopOfArticle = () => window.scrollTo(0, 200)

function App({
  isDesktop,
  selectedArticle
}) {
  const [desktop, setDesktop] = useState(isDesktop)
  const [article, setArticle] = useState(selectedArticle)

  useEffect(() => {
    if (article !== selectedArticle) {
      setDesktop(isDesktop)
      setArticle(selectedArticle)
    }
  }, [isDesktop, selectedArticle, article])

  useEffect(() => {
    if (desktop) scrollToTop()
    else scrollToTopOfArticle()
  }, [desktop, article])

  return (
    <div className="App">
      <h1>{selectedArticle.title}</h1>
      <p>{selectedArticle.body}</p>
    </div>
  )
}

或者,您可以将此锁定行为抽象为另一个挂钩,以便 isDesktop 仅在 selectedArticle 更改时更新其实时值。请注意,selectedArticle 仍然需要成为滚动动作效果的依赖项,因此 useEffect() 将在每次更改为 selectedArticle 时触发滚动动作,即使 isDesktop 没有自上次触发以来更改的值。

const useLatch = (value, deps) => {
  const [state, setState] = useState(value)
  const effect = useCallback(() => { setState(value) }, [value])
  useEffect(effect, deps)
  return state
}

const scrollToTop = () => window.scrollTo(0, 0)
const scrollToTopOfArticle = () => window.scrollTo(0, 200)

function App({
  isDesktop,
  selectedArticle
}) {
  const latchedIsDesktop = useLatch(isDesktop, [selectedArticle])

  useEffect(() => {
    if (latchedIsDesktop) scrollToTop()
    else scrollToTopOfArticle()
  }, [latchedIsDesktop, selectedArticle])

  return (
    <div className="App">
      <h1>{selectedArticle.title}</h1>
      <p>{selectedArticle.body}</p>
    </div>
  )
}