通过 props 方法将子组件的引用传递给父组件

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

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&lt;any&gt; = (props: any) =&gt; {

  const childRef = useRef&lt;any&gt;(null);

  const clickHandler = () =&gt; {
    props.onListItemClick(childRef);
  };

  return (
    &lt;li onClick={clickHandler} ref={childRef}&gt;
      {props.key} - {props.item.name}
    &lt;/li&gt;
  );
};

export const Parent: React.FC&lt;any&gt; = (props: any) =&gt; {

  const listItemClickHandler = (clickedChildRef: any) =&gt; {
    // use the child ref to set the scroll
    if (clickedChildRef &amp;&amp; clickedChildRef.current) {
      clickedChildRef.current.scrollIntoView({ behavior: &#39;smooth&#39;, block: &#39;start&#39; });
    }
  };

  return (
    &lt;ol&gt;
      {props.data.map((item: any, index: number) =&gt;
        &lt;Child item={item} key={index} onListItemClick={listItemClickHandler} /&gt;
      )}
    &lt;/ol&gt;
  )
};

答案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 &#39;react&#39;;
import &#39;./style.css&#39;;
const MyComponent = React.forwardRef(function MyInput(props, ref) {
React.useImperativeHandle(
ref,
() =&gt; {
return {
test: () =&gt; {
console.log(&#39;someProp&#39;, props.someProp);
},
};
},
[]
);
return &lt;div&gt;Hello&lt;/div&gt;;
});
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 (
&lt;&gt;
&lt;div&gt;
&lt;button
onClick={() =&gt; {
itemsRef.current.get(&#39;3&#39;).test();
}}
&gt;
Call the test method for the third item
&lt;/button&gt;
&lt;ul&gt;
{items.map((item) =&gt; (
&lt;MyComponent
key={item.id}
someProp={item.id}
ref={(node) =&gt; {
const map = getMap();
if (node) {
map.set(item.id, node);
} else {
map.delete(item.id);
}
}}
/&gt;
))}
&lt;/ul&gt;
&lt;/div&gt;
&lt;/&gt;
);
}
const items = [];
for (let i = 0; i &lt; 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&lt;any&gt; = (props: any) =&gt; {
const childRef = useRef&lt;any&gt;(null);
const clickHandler = () =&gt; {
props.onListItemClick(childRef);
};
return (
&lt;li onClick={clickHandler} ref={childRef}&gt;
{props.key} - {props.item.name}
&lt;/li&gt;
);
};

When you could just do this:

export const Child: React.FC&lt;any&gt; = (props: any) =&gt; {
const clickHandler = (e) =&gt; {
// although you wouldn&#39;t need to pass it as an object with key current
// it&#39;s just an example to show that useRef isn&#39;t needed
props.onListItemClick({current:e.target});
};
return (
&lt;li onClick={clickHandler}&gt;
{props.key} - {props.item.name}
&lt;/li&gt;
);
};

huangapple
  • 本文由 发表于 2023年5月29日 21:19:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76357742.html
匿名

发表评论

匿名网友

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

确定