React - 在组件中使用 ref 并通过 props 传递给父组件

14
更新:我的问题实际上是由于输错了 - 如果您想在子组件和父组件中都使用子元素引用,则通用方法可以正常工作。
这里有一个可行的示例来展示这种方法的工作方式: https://codesandbox.io/s/rwj7z7o7oo 原始帖子:
我正在尝试将ref转发到父组件,同时还使该ref对子类(它是一个类)中的函数可访问。目前,我可以成功地将ref传递给父级,但是在子级中无法访问该ref。
class Child extends React.Component {
    // Unable to access the forwarded ref here:
    componentDidMount() {
        console.log(this.props.forwardedRef); // null
    }

    render() {
        return <input type="text" ref={this.props.forwardedRef} />
    }
}

// Parent is able to access the ref:
const Parent = () => {
    const childRef = useRef(null);

    function handleClick() {
        console.log(childRef.current); // correctly ref's the input el
    }

    return (
        <Child forwardedRef={childRef} onClick={handleClick} />
    );
}

有没有其他方法可以让我在子组件和父组件中都使用ref?


你希望将子级的引用传递给父级,但是你当前的代码不起作用,对吗? - Shawn Andrews
嗨,肖恩 - 是的,在示例代码中,子代的引用成功地传递给了父代,但是在子代本身中无法访问该引用。 - Stephen
2个回答

13

useRef 返回类似于实例变量的值。在您的情况下,即使您设置了ref,也没有任何东西会导致组件重新渲染,因此子组件的componentDidUpdate不会运行。

另外,您没有从Child组件返回任何内容。

class Child extends React.Component {
  // Unable to access the forwarded ref here:
  componentDidUpdate(prevProps) {
    console.log(this.props.forwardedRef); // null
    console.log(prevProps.forwardedRef); // null
  }

  render() {
    return (
      <React.Fragment>
        <input type="text" ref={this.props.forwardedRef} />
        <div>{this.props.count}</div>
        <input type="button" onClick={this.props.onClick} value={"Click"} />
      </React.Fragment>
    );
  }
}

// Parent is able to access the ref:
const Parent = () => {
  const childRef = useRef(null);
  const [count, setCount] = useState(0);

  function handleClick() {
    console.log(childRef.current); // correctly ref's the input el
    setCount(count => count + 1);
  }

  return <Child forwardedRef={childRef} count={count} onClick={handleClick} />;
};

演示


谢谢Shubham - 我不经常使用类组件,所以犯了一个错误。您可以替换componentDidMount(我编辑了我的原始问题)。我还在Child的render函数中添加了'return',感谢您指出了这个笔误。我试图解决的主要问题是ref在任何子函数中都无法使用。 - Stephen
啊,谢谢Shubham - 你纠正我的错别字帮助我找到了问题所在。我忘记了componentDidUpdate不会在组件的初始渲染时运行。 - Stephen
@Stephen,即使在初始渲染时也会调用componentDidMount,由于父级的ref将更新但没有重新渲染以更新props,因此在componentDidMount中不会反映出来。 - Shubham Khatri
我相信如果父级未传递引用,子级将会出现错误。 - adaf

6

React Hooks

在React Hooks中不需要传递forwardedRef

你可以直接访问ref变量,在forwardRef函数中:

const Child = React.forwardRef((_props, ref) => {
  React.useLayoutEffect(() => {
    if (ref.current) {
      ref.current.insertAdjacentHTML(
        "beforeend",
        "<span>Ref works in child</span>"
      );
    }
  }, [ref]);

  return (
    <div className="child" ref={ref}>
      <h2>Child Component</h2>
    </div>
  );
});

const Parent = () => {
  const childRef = React.useRef(null);

  React.useLayoutEffect(() => {
    if (childRef.current) {
      childRef.current.insertAdjacentHTML(
        "afterbegin",
        "<span>Ref works in parent</span>"
      );
    }
  }, []);

  return (
    <div className="parent">
      <h1>Parent Component</h1>
      <Child ref={childRef} />
    </div>
  );
};

function App() {
  return (
    <div className="App">
      <Parent />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App />,
  rootElement
);
.App {
  font-family: sans-serif;
  text-align: center;
  padding: 1rem;
}

.parent {
  border: 1px dashed red;
  padding: 1rem;
}

.child {
  border: 1px dashed blue;
  padding: 1rem;
}

span {
  display: block;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  margin: 0.5rem;
  color: #777;
  border: 1px dashed #999;
}
<div id="root"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接