英文:
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) => void) | MutableRefObject<T | null> | 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 (
<ChildComponent ref={someData} />
);
}
const ChildComponent = forwardRef<number>(function ChildComponent(props, ref) {
function onClick() {
// use ref here, need the number from ref but get type error
}
return <button onclick={onClick} type="button"></button>;
}
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<T | null> | 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) => void)
or MutableRefObject<T | null>
or null
.
This would be one way to extract its value:
function onClick() {
let extracted: number | null=null;
if (ref) { // TypeScript infers it's not null inside the clause
if (typeof ref === 'function') { // TypeScript infers it's a function
ref(1); // not sure what that actually does, I guess it's a setter when you pass a "callback" with ref = React.createRef()
} else { // TypeScript infers there's only one possibily left: it has the "current" 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()=> {
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>;
}
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'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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论