【问题标题】:How do I pass ref to a neighbour component如何将 ref 传递给邻居组件
【发布时间】:2021-03-14 20:42:50
【问题描述】:

我正在尝试在我的 Header 组件中的搜索输入上使用 ref,这不是我的 ResultsList 组件的高阶组件。我想将焦点放在来自 ResultsList 组件的 Header 的搜索输入上。标题很直观,因为我所要做的就是下面。如果我想在 ResultsList 中创建一个专注于 Header 中的输入元素的按钮怎么办?我如何通过这个参考?我已阅读有关 forwardRef 的信息,但我没有将我的 ref 转发。 ResultsList 不是 Header 的子级。

import React, { useState, useRef } from 'react';
import { useHistory } from 'react-router-dom';

const Header = () => {
  const searchInput = useRef(null);
  const history = useHistory();

  const [searchValue, setSearchValue] = useState(keyword);

  function handleChange(event) {
    setSearchValue(event.target.value);
  }

  function handleSearch(event) {
    event.preventDefault();
    if(searchValue) {
      history.push(`/search/${searchValue}`);
    } else {
      searchInput.current.focus();
    }
  }

  return (
    <form onSubmit={handleSearch} role="search">
      <input
        value={searchValue}
        onChange={handleChange}
        className="HeaderSearch__input"
        id="header-search-input"
        placeholder="Search a repository"
        ref={searchInput}>
      </input>
    </form>
  );
}

export default Header;

我的 App 组件如下所示

import React from 'react';
import Header from './Header';
import ResultsList from './ResultsList';

function App() {
  return (
    <>
      <Header />
      <ResultsList />
    </>
  );
}

export default App;

【问题讨论】:

  • @SakoBu 好吧,我确实在描述中提到了这一点,但我添加的按钮不是 Header 的子级。
  • 如果我做对了,你可以用 React Context 来做。请查看以下demoForwardRef 也可以使用,但您需要将它传递给两个组件 - 请参阅演示 here。我更喜欢使用 Context 但两者都可以。

标签: reactjs react-hooks use-ref


【解决方案1】:

您将需要使用“提升状态”模式。在 App 中声明 react ref 并将其传递给两个组件,传递给 Header 以将 ref 附加到节点,并传递给 ResultsList 以访问 ref 并设置“焦点”。

function App() {
  const searchInputRef = useRef(null);
  return (
    <>
      <Header searchInputRef={searchInputRef} />
      <ResultsList searchInputRef={searchInputRef} />
    </>
  );
}

附加并使用您在Header中的引用

const Header = ({ searchInputRef }) => {
  const history = useHistory();

  const [searchValue, setSearchValue] = useState(keyword);

  function handleChange(event) {
    setSearchValue(event.target.value);
  }

  function handleSearch(event) {
    event.preventDefault();
    if(searchValue) {
      history.push(`/search/${searchValue}`);
    } else {
      searchInputRef.current.focus();
    }
  }

  return (
    <form onSubmit={handleSearch} role="search">
      <input
        value={searchValue}
        onChange={handleChange}
        className="HeaderSearch__input"
        id="header-search-input"
        placeholder="Search a repository"
        ref={searchInputRef}>
      </input>
    </form>
  );
}

同样,您也可以在ResultsList 组件中访问searchInputRef

function ResultsList({ searchInputRef }) {

  ...

  <button
    type="button"
    onClick={() => searchInputRef.current?.focus()}
  >
    Set Search Focus
  </button>
}

编辑

如果嵌套更深的组件需要 ref 怎么办?

如果子组件不是直接后代,那么您可以利用反应上下文来允许子组件访问 ref 而无需通过 React 树将其作为 prop 传递。

创建和导出上下文。

const SearchInputRefContext = React.createContext(null);

App中的孩子提供上下文

import SearchInputRefContext from '.....';

function App() {
  const searchInputRef = useRef(null);
  return (
    <SearchInputRefContext.Provider value={searchInputRef}>
      <Header />
      <ResultsList />
    </SearchInputRefContext.Provider>
  );
}

访问任何子子组件中的上下文

const Header = () => {
  const history = useHistory();

  const searchInputRef = useContext(SearchInputRefContext);

  const [searchValue, setSearchValue] = useState(keyword);

  function handleChange(event) {
    setSearchValue(event.target.value);
  }

  function handleSearch(event) {
    event.preventDefault();
    if(searchValue) {
      history.push(`/search/${searchValue}`);
    } else {
      searchInputRef.current.focus();
    }
  }

  return (
    <form onSubmit={handleSearch} role="search">
      <input
        value={searchValue}
        onChange={handleChange}
        className="HeaderSearch__input"
        id="header-search-input"
        placeholder="Search a repository"
        ref={searchInputRef}>
      </input>
    </form>
  );
}

无论嵌套多深

function ReallyDeepComponent() {
  const searchInputRef = useContext(SearchInputRefContext);

  ...

  <button
    type="button"
    onClick={() => searchInputRef.current?.focus()}
  >
    Set Search Focus
  </button>
}

如果您碰巧仍在使用基于类的组件,请参阅此section

【讨论】:

  • 是的,我想到了,但是如果我的 ResultsList 嵌入到层次结构的更深处,比如 ResultsPage > ResultsList 怎么办?我必须将它传递给道具两次吗?这是一种常见的做法吗?
  • @LazioTibijczyk 将道具向下传递一个级别是很常见的,可能不太常见到另一个级别,或者更多。这种模式称为“道具钻孔”,您可以在其中将道具传递很多层。 react 提供的解决方案是Context API,它“提供了一种通过组件树传递数据的方法,而无需在每个级别手动传递道具。”您可以创建一个提供共享 ref 的上下文,并且组件,只要它们位于提供者的子树中,就可以访问上下文值。
  • @LazioTibijczyk 如果有兴趣,我可以更新我的答案,并尝试提供有关它的更多详细信息。
  • @LazioTibijczyk 当然可以。我不觉得它不合理,因为它解决了“支柱钻孔”的问题。我想你会发现代码非常简洁。
  • @LazioTibijczyk Optional Chaining Operator。它基本上是一个空检查,所以searchInputRef.current?.focus() 等价于searchInputRef.current &amp;&amp; searchInputRef.current.focus()
猜你喜欢
  • 2021-05-28
  • 2017-08-16
  • 1970-01-01
  • 2022-12-18
  • 2020-05-07
  • 1970-01-01
  • 2020-10-21
  • 2020-03-05
  • 2021-12-21
相关资源
最近更新 更多