英文:
Passing child ref to a parent through a props method
问题
我遇到了一种情况,需要将子组件中元素的引用传递给父组件。我无法使用 "forwardRef" 方法来实现这一点,因为我在父组件中渲染了一组子组件。
我将子组件中的引用作为方法参数传递给了一个可以通过 props 访问的父组件方法。这种方法有效,我能够在父组件中访问引用以实现我想要的效果。
我想知道这种方法是否会导致将来的问题,或者是否属于反模式。请告诉我是否可以通过其他方式实现相同的效果。
以下是演示我的实现的示例代码:
export const Child: React.FC<any> = (props: any) => {
const childRef = useRef<any>(null);
const clickHandler = () => {
props.onListItemClick(childRef);
};
return (
<li onClick={clickHandler} ref={childRef}>
{props.key} - {props.item.name}
</li>
);
};
export const Parent: React.FC<any> = (props: any) => {
const listItemClickHandler = (clickedChildRef: any) => {
// 使用子组件的引用来设置滚动
if (clickedChildRef && clickedChildRef.current) {
clickedChildRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
};
return (
<ol>
{props.data.map((item: any, index: number) =>
<Child item={item} key={index} onListItemClick={listItemClickHandler} />
)}
</ol>
)
};
希望这对你有帮助。
英文:
I have encountered a scenario where I need to pass the ref of an element in child to the parent. I could not do this using the "forwardRef" approach as I render a list of children in the parent component.
I have passed the ref from the child as a method parameter to one of the parent method which is accessible via props. This works and I was able to access the ref in the parent to achieve what I wanted.
I want to know if this approach would result in any future problem or if it is an anti-pattern. Please let me know if the same can be achieved through any other means.
Below is a sample code demonstrating my implementation.
export const Child: React.FC<any> = (props: any) => {
const childRef = useRef<any>(null);
const clickHandler = () => {
props.onListItemClick(childRef);
};
return (
<li onClick={clickHandler} ref={childRef}>
{props.key} - {props.item.name}
</li>
);
};
export const Parent: React.FC<any> = (props: any) => {
const listItemClickHandler = (clickedChildRef: any) => {
// use the child ref to set the scroll
if (clickedChildRef && clickedChildRef.current) {
clickedChildRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
};
return (
<ol>
{props.data.map((item: any, index: number) =>
<Child item={item} key={index} onListItemClick={listItemClickHandler} />
)}
</ol>
)
};
答案1
得分: 1
Your approach might work, not sure. But there is an official example for working with a list of refs (here I have adapted it for custom components):
import React from 'react';
import './style.css';
const MyComponent = React.forwardRef(function MyInput(props, ref) {
React.useImperativeHandle(
ref,
() => {
return {
test: () => {
console.log('someProp', props.someProp);
},
};
},
[]
);
return <div>Hello</div>;
});
export default function App() {
const itemsRef = React.useRef(null);
function getMap() {
if (!itemsRef.current) {
// Initialize the Map on first usage.
itemsRef.current = new Map();
}
return itemsRef.current;
}
return (
<>
<div>
<button
onClick={() => {
itemsRef.current.get('3').test();
}}
>
Call the test method for the third item
</button>
<ul>
{items.map((item) => (
<MyComponent
key={item.id}
someProp={item.id}
ref={(node) => {
const map = getMap();
if (node) {
map.set(item.id, node);
} else {
map.delete(item.id);
}
}}
/>
))}
</ul>
</div>
</>
);
}
const items = [];
for (let i = 0; i < 10; i++) {
items.push({
id: i.toString(),
});
}
英文:
> I want to know if this approach would result in any future problem or
> if it is an anti-pattern. Please let me know if the same can be
> achieved through any other means.
Your approach might work, not sure. But there is official example for working with list of refs (here I have adapted it for custom components):
import React from 'react';
import './style.css';
const MyComponent = React.forwardRef(function MyInput(props, ref) {
React.useImperativeHandle(
ref,
() => {
return {
test: () => {
console.log('someProp', props.someProp);
},
};
},
[]
);
return <div>Hello</div>;
});
export default function App() {
const itemsRef = React.useRef(null);
function getMap() {
if (!itemsRef.current) {
// Initialize the Map on first usage.
itemsRef.current = new Map();
}
return itemsRef.current;
}
return (
<>
<div>
<button
onClick={() => {
itemsRef.current.get('3').test();
}}
>
Call the test method for the third item
</button>
<ul>
{items.map((item) => (
<MyComponent
key={item.id}
someProp={item.id}
ref={(node) => {
const map = getMap();
if (node) {
map.set(item.id, node);
} else {
map.delete(item.id);
}
}}
/>
))}
</ul>
</div>
</>
);
}
const items = [];
for (let i = 0; i < 10; i++) {
items.push({
id: i.toString(),
});
}
答案2
得分: 1
对于长存性而言,这里的useRef
是多余的,这两个代码片段在功能上是等效的:
export const Child: React.FC<any> = (props: any) => {
const childRef = useRef<any>(null);
const clickHandler = () => {
props.onListItemClick(childRef);
};
return (
<li onClick={clickHandler} ref={childRef}>
{props.key} - {props.item.name}
</li>
);
};
当你可以简单地这样做:
export const Child: React.FC<any> = (props: any) => {
const clickHandler = (e) => {
// 尽管你不需要将它作为一个带有 key 属性的对象传递
// 这只是一个示例,用来说明不需要 useRef
props.onListItemClick({ current: e.target });
};
return (
<li onClick={clickHandler}>
{props.key} - {props.item.name}
</li>
);
};
英文:
For posterity, useRef
here is superfluous, the two snippets are equivalent in functionality:
export const Child: React.FC<any> = (props: any) => {
const childRef = useRef<any>(null);
const clickHandler = () => {
props.onListItemClick(childRef);
};
return (
<li onClick={clickHandler} ref={childRef}>
{props.key} - {props.item.name}
</li>
);
};
When you could just do this:
export const Child: React.FC<any> = (props: any) => {
const clickHandler = (e) => {
// although you wouldn't need to pass it as an object with key current
// it's just an example to show that useRef isn't needed
props.onListItemClick({current:e.target});
};
return (
<li onClick={clickHandler}>
{props.key} - {props.item.name}
</li>
);
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论