React - 在组件中使用 ref 并将其传递给 props 中的父级

React - use ref in component AND pass it to parent in props

更新:我的问题实际上是由于拼写错误造成的——如果您想在子组件和父组件中都使用子元素 ref,则一般方法可以正常工作。

这是一个有效方法的工作示例: https://codesandbox.io/s/rwj7z7o7oo


原文post:

我正在尝试将 ref 转发给父组件,同时也使子组件(即 class)中的函数可以访问该 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} />
    );
}

是否有另一种方法可以让我在 Child 和 Parent 中使用 ref?

useRef returns 类似于实例变量的值 类。在您的情况下,即使您设置了 ref 也不会导致组件呈现,因此子组件的 componentDidUpdate 不会 运行.

此外,您还没有从子组件返回任何内容。

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} />;
};

Working demo

反应挂钩

不需要在 React hooks 中传递 forwardedRef

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

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>