当前位置: 首页 > 知识库问答 >
问题:

当我想在挂载上始终运行它一次时,USEPERP已经错过了依赖项

凌朗
2023-03-14

我有一个组件,它应该从组件中读取属性(要么是字符串“fill”,要么是字符串“stroke”),然后从对象中提取相应的键来读取它的上下文。

一旦选择了对象,就会装入该对象,访问活动对象并将其颜色提取为填充颜色或笔划颜色。

useEffect(() => {
    setButtonColor(context.objects.getActiveObject()[props.type]);
}, []); //on mount

这样安装:

<ColorPicker type="fill" />
<ColorPicker type="stroke" />

这应该只在坐骑上运行一次。我认为当dep数组为空时,它在任何情况下都会在装入时运行一次。

那么,如何利用道具和上下文在坐骑上运行一次?

当我希望它总是在挂载上只运行一次时,为什么它需要依赖关系呢?

共有2个答案

凤自珍
2023-03-14

这是我解决这个问题的方法:

将颜色设置为变量,然后使用该变量设置组件安装时的按钮颜色。

const oldColor = useRef(context.objects.getActiveObject()[props.type]);

useEffect(() => {
    setButtonColor(oldColor.current);
}, []); //on mount

useRef返回一个可变的ref对象,其。当前属性初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内持续存在。

秋煌
2023-03-14

最好不要认为效果在组件生命周期的某些点上运行。虽然这是真的,但一个可能有助于您更好地掌握钩子的模型是依赖数组是效果与之同步的事物列表:也就是说,每次这些事物发生变化时都应该运行效果。

当您得到一个指示依赖项数组缺少道具的linter错误时,linter试图告诉您的是,您的效果(或回调,或记忆函数)依赖于不稳定的值。它之所以这样做,是因为这往往是一个错误。考虑以下事项:

function C({ onSignedOut }) {
  const onSubmit = React.useCallback(() => {
    const response = await fetch('/api/session', { method: 'DELETE' })
    if (response.ok) {
      onSignedOut()
    }
  }, [])

  return <form onSubmit={onSubmit}>
    <button type="submit">Sign Out</button>
  </form>
}

linter将为onSubmit中的依赖数组发出警告,因为onSubmit取决于onSignedOut的值。如果您将此代码保持原样,那么onSubmit将仅使用第一个值onSignedOut创建一次。如果onSignedOutprop更改,onSubmit将不会反映此更改,并且您最终将获得对onSignedOut的过时引用。这一点在这里得到了最好的证明:

import { render } from "@testing-library/react"

it("should respond to onSignedOut changes correctly", () => {
  const onSignedOut1 = () => console.log("Hello, 1!")
  const onSignedOut2 = () => console.log("Hello, 2!")
  const { getByText, rerender } = render(<C onSignedOut={onSignedOut1} />)
  getByText("Sign Out").click()
  // stdout: Hello, 1!
  rerender(<C onSignedOut={onSignedOut2} />)
  getByText("Sign Out").click()
  // stdout: Hello, 1!
})

控制台。log()语句不更新。对于这个特定的示例,这可能会违反您作为组件消费者的期望。

现在让我们看看你的代码。

正如您所看到的,此警告本质上是说明您的代码可能没有html" target="_blank">执行您认为它正在执行的操作。如果您确信自己知道正在执行的操作,则消除警告的最简单方法是禁用该特定行的警告。

useEffect(() => {
    setButtonColor(context.objects.getActiveObject()[props.type]);
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); //on mount

正确的方法是将依赖项放在数组中。

const { type } = props
useEffect(() => {
    setButtonColor(context.objects.getActiveObject()[type]);
}, [context, type]);

但是,这将在每次更改type时更改按钮颜色。这里需要注意的是:您正在设置状态以响应道具的变化。这被称为派生状态。

您只希望在初始挂载时设置该状态。因为您只想在初始挂载上设置它,所以您可以简单地将您的值传递给React.use状态(初始状态),这将完全实现您想要的:

function C({ type }) {
  const initialColor = context.objects.getActiveObject()[type];
  const [color, setButtonColor] = React.useState(initialColor);
  ...
}

这仍然会留下一个问题,消费者可能会混淆为什么视图在您更改道具时会更新。在功能组件起飞之前(我仍然使用的一个),常见的惯例是用单词初始作为不被监控的道具的前缀:

function C({ initialType }) {
  const initialColor = context.objects.getActiveObject()[initialType];
  const [color, setButtonColor] = React.useState(initialColor);
}

不过,在这里您仍然应该小心:这确实意味着,在C的生命周期中,它只能从上下文初始类型读取一次。如果上下文的值发生变化怎么办?您可能会在

反应。useRef()确实是一个通过只捕获初始版本来稳定值的很好的解决方案,但是对于这个用例来说它不是必需的。

 类似资料: