如果我不拥有该代码,如何将 Ref 分配给 React 组件中的按钮?我想集中注意力

Posted

技术标签:

【中文标题】如果我不拥有该代码,如何将 Ref 分配给 React 组件中的按钮?我想集中注意力【英文标题】:How to assign a Ref to a button in a React component if I don't own that code? I want to focus it 【发布时间】:2022-01-04 09:47:12 【问题描述】:

我正在研究 React。还有一个团队向我们展示了一个组件,其中有一个按钮。它是我们存储库的依赖项。我希望在完成特定操作时使该按钮成为焦点。我想使用 refs 或任何其他可接受的方式来做到这一点。

myCode.js

focusExternalButton() 
  // some code that focuses the button in the external component


render() 

  return 
    <div>
      <ExternalButtonComponent/>
      <button onClick=this.focusExternalButton>submit</button>
    </div>
  
 

ExternalButtonComponent.js

render() 
  return <div><button id="btn-external">This is a button</button></div>

当我点击我的按钮时,如何将焦点放在外部按钮上?我正在考虑参考,但我不确定如何实现它。

【问题讨论】:

按钮部分属于哪个库?你读过文档吗?很可能已经有一个内置的解决方案。 该库归我组织中的另一个团队所有。我可以要求他们为此公开一个参考。但在我联系他们之前,我只想确保没有其他方法。 @pilchard 您不能使用 ref,因为如果按钮组件未配置为接收它,它将被按钮组件忽略。此外,不能保证返回的 html 的内部结构会保持一致。实现它的一个 hacky 方法是查询 useLayoutEffect 中的按钮,但如果您可以选择让 ref 公开,那将是 much 更可取的。 @pilchard 你对使用 document.getElementById('target-id').focus() 有什么想法?这样可以集中注意力吗? 【参考方案1】:

外部组件使用ids 让我有点吃惊,因为在一个页面中多次渲染它会导致页面上出现重复的ids,这不是有效的 HTML。

不过,是的,您可以直接查询 DOM,不过我可能会在挂载时执行此操作,并将返回的结果元素存储在 ref 中。

const  useEffect, useRef  = React;

function App() 
  const buttonRef = useRef();

  useEffect(() => 
    buttonRef.current = document.getElementById('btn-external');
  , []);

  function focusExternalButton() 
    buttonRef.current.focus();
  ;

  return (
    <div>
      <ExternalButtonComponent label="This is a button" />
      <button type='button' onClick=focusExternalButton>submit</button>
    </div>
  );


function ExternalButtonComponent(label) 
  return <div><button id="btn-external">label</button></div>;


ReactDOM.render(
  <App />,
  document.getElementById("root")
);
#btn-external:focus  box-shadow: 0 0 0 3px rgba(21, 156, 228, 0.4);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

但是,为了避免每次使用组件时都必须这样做,您可以将外部组件和forward the ref 包装起来。

const WrappedExternalButtonComponent = forwardRef((props, ref) => 
  useEffect(() => 
    ref.current = document.getElementById('btn-external');
  , []);

  return <ExternalButtonComponent ...props />
);

const  useEffect, useRef, forwardRef  = React;

const WrappedExternalButtonComponent = forwardRef((props, ref) => 
  useEffect(() => 
    ref.current = document.getElementById('btn-external');
  , []);

  return <ExternalButtonComponent ...props />
);

function ExternalButtonComponent(label) 
  return <div><button id="btn-external">label</button></div>;


function App() 
  const buttonRef = useRef();

  function focusExternalButton() 
    buttonRef.current.focus();
  ;

  return (
    <div>
      <WrappedExternalButtonComponent ref=buttonRef label="This is a button" />
      <button type='button' onClick=focusExternalButton>submit</button>
    </div>
  );


ReactDOM.render(
  <App />,
  document.getElementById('root')
);
#btn-external:focus  box-shadow: 0 0 0 3px rgba(21, 156, 228, 0.4);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

甚至可以通过创建一个实用程序来进一步概括它,该实用程序将Component 和一个查询函数作为参数并返回一个包装的组件。

const wrapExternalComponent = (Component, query) => forwardRef(( children, ...props , ref) => 
  useEffect(() => 
    ref.current = query();
  , []);

  return (
    <Component ...props>
      children
    </Component>
  );
);

const WrappedExternalButtonComponent = 
  wrapExternalComponent(ExternalButtonComponent, () => document.getElementById('btn-external'));

const  useEffect, useRef, forwardRef  = React;

const wrapExternalComponent = (Component, query) => forwardRef(( children, ...props , ref) => 
  useEffect(() => 
    ref.current = query();
  , []);

  return (
    <Component ...props>
      children
    </Component>
  );
);

function ExternalButtonComponent(label) 
  return <div><button id="btn-external">label</button></div>;


const WrappedExternalButtonComponent = 
  wrapExternalComponent(ExternalButtonComponent, () => document.getElementById('btn-external'));

function App() 
  const buttonRef = useRef();

  function focusExternalButton() 
    buttonRef.current.focus();
  ;

  return (
    <div>
      <WrappedExternalButtonComponent ref=buttonRef label="This is a button" />
      <button type='button' onClick=focusExternalButton>submit</button>
    </div>
  );






ReactDOM.render(
  <App />,
  document.getElementById('root')
);
#btn-external:focus  box-shadow: 0 0 0 3px rgba(21, 156, 228, 0.4);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

【讨论】:

感谢您的回答!非常有帮助。关于您对 id 感到惊讶 - 它实际上是基于道具动态生成的,但我在尝试简化时错过了提及。 啊,我觉得这更有意义,很乐意提供帮助。

以上是关于如果我不拥有该代码,如何将 Ref 分配给 React 组件中的按钮?我想集中注意力的主要内容,如果未能解决你的问题,请参考以下文章

为啥“ref”没有将更新的值分配给下一个字段?

是否可以将焦点应用于我的 React 组件之一内的输入元素,而无需直接将 ref 分配给输入标签?

如何使用for循环将返回值分配给变量

您如何将变量分配给持久数据?

如何有效地将数字字符串值分配给整数? [复制]

如何将指针分配给函数中的新对象,而该对象在退出后不会消失[重复]