React:如何在子组件中使用前向引用中的数据

huangapple go评论71阅读模式
英文:

React: How to use the data in a forward ref in a child component

问题

根据React的文档,useRef() 用于在不影响渲染的情况下使用数据,并且要将ref传递给子组件,你需要在子组件周围使用forwardRef包装器。

我正在使用React和TypeScript,在尝试将ref传递给我的子组件时遇到了问题。我遇到的问题是子组件中的ref类型是((instance: T | null) => void) | MutableRefObject<T | null> | null;,这并没有明确说明如何提取ref的值。

示例:


function ParentComponent() {
  const someData = useRef(1);
  // 一些可能会改变someData值的事件处理
  return (
    <ChildComponent ref={someData} />
 );
}


const ChildComponent = forwardRef<number>(function ChildComponent(props, ref) {
  function onClick() {
    // 在这里使用ref,需要从ref获取数字但会得到类型错误
  }

  return <button onclick={onClick} type="button"></button>;
}

虽然我理解ref的主要用例是处理DOM引用以进行操作,但ref的核心目的是包含在更改时不应影响渲染的数据。

是否有一种不影响渲染的方法将数据传递给子组件,这是不是不应该做的事情,还是我只需要将其转换为 MutableRefObject<T | null> | null 并结束呢?我担心如果需要对类型进行任何处理,会影响函数签名。在搜索时,我找到的所有内容都只是讨论在两者之间传递DOM ref,而不是实际数据。

英文:

According to React's documentation, useRef() is for when you need data that doesn't impact rendering and to pass a ref to a child you have to use forwardRef wrapper around child component.

I am using react and typescript and run into a problem trying to pass ref to my child component. The problem I run into is the type of the ref in the child component is ((instance: T | null) =&gt; void) | MutableRefObject&lt;T | null&gt; | null; This doesn't provide a clear way of how to extract the value of the ref.

Example:


function ParentComponent() {
  const someData = useRef(1);
  // some event handling who may change the value of someData
  return (
    &lt;ChildComponent ref={someData} /&gt;
 );
}


const ChildComponent = forwardRef&lt;number&gt;(function ChildComponent(props, ref) {
  function onClick() {
    // use ref here, need the number from ref but get type error
  }

  return &lt;button onclick={onClick} type=&quot;button&quot;&gt;&lt;/button&gt;;
}

While I understand the main use case for ref is to handle DOM references for manipulations, the core purpose of ref is to contain data that shouldn't impact rendering when changed.

Is there a different way to pass data that shouldn't impact rendering to a child component, is this just not something supposed to be done, or do I just need to cast it as MutableRefObject&lt;T | null&gt; | null and call it a day? I was worried about the function signature on the type if I needed to do anything with that. Everything I find while searching just talks about passing a DOM ref between the two not actual data.

答案1

得分: 1

如你所指出的,ref 可以是不同的类型,因此 TypeScript 不知道要提供什么建议。它可以是 ((instance: T | null) => void)MutableRefObject<T | null>null

这是提取其值的一种方式:

function onClick() {
  let extracted: number | null = null;
  if (ref) { // TypeScript 推断它在条件内不是 null
    if (typeof ref === 'function') { // TypeScript 推断它是一个函数
      ref(1); // 不太确定它实际上做了什么,我猜当你传递一个“回调”给 ref = React.createRef() 时,它是一个 setter
    } else { // TypeScript 推断只剩下一种可能性:它具有“current”属性。
      extracted = ref.current;
    }
  }
  console.log(extracted)
}

因为你控制着 ChildCompoent,所以你不需要真的包装它。你可以只去掉 forwardRef 的魔法,然后将 someData 作为一个普通对象传递,可以像这样进行处理:

function ParentComponent() {
  const someData = useRef(1);
  console.log("rendering parent")
  useEffect(() => {
    const timer = setTimeout(() => {
      someData.current = 2
    }, 1000);
    return () => clearTimeout(timer);
  }, [])
  return (
    <ChildComponent someData={someData}/>
  );
}

function ChildComponent(ref: { someData: MutableRefObject<number> }) {
  function onClick() {
    console.log("clicked with no:", ref.someData.current)
  }

  console.log("rendering child")
  return <button onClick={onClick} type='button'></button>;
}

将会打印:

rendering parent // 在开发环境中可能出现两次,但只因为你在开发环境中运行它
rendering child // 如上所述
clicked with no: 1 // 如果你足够快
clicked with no: 2 

useRef 的整个理念就是你只是传递了一个值的引用(即 someData.current),因此可以在不引起组件更新(因为 someData 仍然是同一个对象)的情况下进行操作,而不会触发重新渲染。因为没有更多的东西需要考虑,所以你可以以任何你喜欢的方式传递它。

英文:

As you've pointed out, ref can be of different types, so TypeScript doesn't know what to suggest. It can be either ((instance: T | null) =&gt; void) or MutableRefObject&lt;T | null&gt; or null.
This would be one way to extract its value:

  function onClick() {
    let extracted: number | null=null;
    if (ref) { // TypeScript infers it&#39;s not null inside the clause
      if (typeof ref === &#39;function&#39;) { // TypeScript infers it&#39;s a function
        ref(1); // not sure what that actually does, I guess it&#39;s a setter when you pass a &quot;callback&quot; with ref = React.createRef()
      } else { // TypeScript infers there&#39;s only one possibily left: it has the &quot;current&quot; property.
        extracted = ref.current;
      }
    }
    console.log(extracted)
  }

Since you're in control of the ChildCompoent, you don't really need to wrap it, though. You can just strip the forwardRef magic and just pass someData as a regular object which can be processed like that:

function ParentComponent()=&gt; {
    const someData = useRef(1);
    console.log(&quot;rendering parent&quot;)
    useEffect(() =&gt; {
        const timer = setTimeout(() =&gt; {
            someData.current = 2
        }, 1000);
        return () =&gt; clearTimeout(timer);
    }, [])
    return (
        &lt;ChildComponent someData={someData}/&gt;
    );
}


function ChildComponent(ref: { someData: MutableRefObject&lt;number&gt; }) {
    function onClick() {
        console.log(&quot;clicked with no:&quot;, ref.someData.current)
    }

    console.log(&quot;rendering child&quot;)
    return &lt;button onClick={onClick} type=&#39;button&#39;&gt;&lt;/button&gt;;
}

will log

rendering parent // might be appear twice but only b/c you run it in dev environment
rendering child // as above
clicked with no: 1 // if you&#39;re fast enough
clicked with no: 2 

The whole idea behind useRef is just that you just that you're passing a reference to a value (i.e. someData.current) and therefore can manipulate it with without the engine noticing a component update (b/c someData remains still the very same object) and therefore without starting a rerender. Since there's nothing more to it, you can pass it around in any way you please.

huangapple
  • 本文由 发表于 2023年7月11日 06:25:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657700.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定