【问题标题】:How do I access refs of a child component in the parent component如何访问父组件中子组件的引用
【发布时间】:2016-10-05 10:46:59
【问题描述】:

如果我有类似的东西

<Parent>
  <Child1 />
  <Child2 />
  <Child3 />
</Parent>

我想从Child2 访问我有refs="child2refs",我该怎么做?

【问题讨论】:

  • 你能在这里发布你的反应代码吗? this.props.children 在这种情况下应该总是用词...
  • 为了访问child我们可以添加ref到它并通过this.refs.child访问它。这不适用于连接的组件(连接到 redux 或其他插件的组件)。我们需要使用 getWrappedInstance() 来获取包装的实例,然后我们可以访问该组件的状态、引用和方法。这是解释它的视频 - youtu.be/VpdKjocgCtA
  • 现在可以转发refs了:reactjs.org/docs/forwarding-refs.html
  • @DaveF 但是你什么时候查询Selector,该元素多久会被替换?至少可以说不可靠。

标签: reactjs


【解决方案1】:

推荐用于 16.3 之前的 React 版本

如果无法避免,从React docs 中提取的建议模式将是:

import React, { Component } from 'react';

const Child = ({ setRef }) => <input type="text" ref={setRef} />;

class Parent extends Component {
    constructor(props) {
        super(props);
        this.setRef = this.setRef.bind(this);
    }

    componentDidMount() {
        // Calling a function on the Child DOM element
        this.childRef.focus();
    }

    setRef(input) {
        this.childRef = input;
    }

    render() {
        return <Child setRef={this.setRef} />
    }
}

Parent 将函数作为绑定到 Parent's this 的 prop 转发。当 React 调用 Child's ref prop setRef 时,它会将 Child's ref 分配给 Parent's childRef 属性。

推荐用于 React >= 16.3

Ref 转发是一种可选功能,它允许某些组件获取它们接收到的 ref,并将其进一步向下传递(换句话说,“转发”它)给子级。

我们创建组件,通过React.forwardRef 转发他们的ref。 返回的 Component ref prop 必须与 React.createRef 的返回类型相同。每当 React 挂载 DOM 节点时,使用 React.createRef 创建的 ref 的属性 current 将指向底层 DOM 节点。

import React from "react";

const LibraryButton = React.forwardRef((props, ref) => (
  <button ref={ref} {...props}>
    FancyButton
  </button>
));

class AutoFocus extends React.Component {
  constructor(props) {
    super(props);
    this.childRef = React.createRef();
    this.onClick = this.onClick.bind(this);
  }

  componentDidMount() {
    this.childRef.current.focus();
  }

  onClick() {
    console.log("fancy!");
  }

  render() {
    return <LibraryButton onClick={this.onClick} ref={this.childRef} />;
  }
}

转发 refs HOC 示例

创建的组件正在将它们的ref 转发到子节点。

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

请参阅 React 文档中的 Forwarding Refs

【讨论】:

  • 从父级向下传递 Ref 似乎与从父级访问子 ref 不同
  • 这不是我想要的例子。 LibraryButton 不是 class。你能在class LibraryButton extends React.Component 中展示例子吗?谢谢。
  • 在 HOC 和连接组件中转发 refs 的情况下,我认为提及这样做的传统方法是在连接方法 connect(mapStateToProps, null, null, { withRef: true })(Cmp); 上使用 options 参数并访问包装的例如,具有this.wrappedComponentRef.current.getWrappedInstance() 的 HOC 中的组件实例。请参阅在连接的组件上使用 refs here 部分
【解决方案2】:
  1. 在子组件内添加一个引用到你需要的节点
  2. 在父组件内部为子组件添加一个引用。
/*
* Child component
*/
class Child extends React.Component {
  render() {
    return (
      <div id="child">
        <h1 ref={(node) => { this.heading = node; }}>
          Child
        </h1>
      </div>
    );
  }
}

/*
 * Parent component
 */
class Parent extends React.Component {
  componentDidMount() {
    // Access child component refs via parent component instance like this
    console.log(this.child.heading.getDOMNode());
  }

  render() {
    return (
      <div>
        <Child
          ref={(node) => { this.child = node; }}
        />
      </div>
    );
  }
}

演示:https://codepen.io/itsfadnis/pen/aLWVVx?editors=0011

【讨论】:

  • 不确定它是否依赖于版本号,但在 React getDOMNode() 的情况下做到这一点,而只需使用 this.child.heading
  • @IanJMiller ref 以this.child.heading 访问,在示例中我只是打印与其关联的 dom 节点
  • 这是什么版本的 React?
  • 不错。这个实现比 React.forwardRef() 更直接
  • 对于一个孩子的孩子?比方说,在 &lt;Child&gt; 内部呈现的组件我如何从 Parent 中引用它?
【解决方案3】:

首先使用:this.props.children 访问子级,然后每个子级将拥有其ref 作为其上的属性。

【讨论】:

  • 如果我从父组件渲染控制台.log(this.props.children),它显示未定义:(
  • 链接它...应该不难发现,
  • 这是推荐的 ReactJS 实践吗?
  • ref 属性是 null 在孩子身上
【解决方案4】:

这是一个使用 refs 关注输入的示例(在 React 16.8.6 中测试):

子组件:

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return (<input type="text" ref={this.myRef} />);
  }
}

父组件和子组件在里面:

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.childRef = React.createRef();
  }
  componentDidMount() {
    this.childRef.current.myRef.current.focus();
  }
  render() {
    return <Child ref={this.childRef} />;
  }
}

ReactDOM.render(
    <Parent />,
    document.getElementById('container')
);

带有 this.props.children 的父组件:

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.childRef = React.createRef();
    }
    componentDidMount() {
        this.childRef.current.myRef.current.focus();
    }
    render() {
        const ChildComponentWithRef = React.forwardRef((props, ref) =>
            React.cloneElement(this.props.children, {
                ...props,
                ref
            })
        );
        return <ChildComponentWithRef ref={this.childRef} />
    }
}

ReactDOM.render(
    <Parent>
        <Child />
    </Parent>,
    document.getElementById('container')
);

【讨论】:

  • 第三个例子是 IT!
  • 无类型打字稿。你的this.props.children是什么类型的?
【解决方案5】:

使用 Ref 转发,您可以将 ref 从父级传递到子级。

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
  1. 通过调用 React.createRef 创建一个 React ref 并将其分配给一个 ref 变量。
  2. 通过将您的 ref 指定为 JSX 属性来传递给它。
  3. React 将 ref 作为第二个参数传递给 forwardRef 内的 (props, ref) => ... 函数。
  4. 将此 ref 参数转发到通过将其指定为 JSX 属性。
  5. 当 ref 被附加时,ref.current 将指向 DOM 节点。

注意 第二个 ref 参数仅在您使用 React.forwardRef 调用定义组件时存在。常规函数或类组件不接收 ref 参数,并且 ref 在 props 中也不可用。

Ref 转发不限于 DOM 组件。您也可以将 refs 转发到类组件实例。

参考:React 文档。

【讨论】:

    【解决方案6】:

    我认为本指南解释得很好https://github.com/yannickcr/eslint-plugin-react/issues/678

    class Field extends Component {
      const { inputRef } = this.props;
      render() {
        return (
          <input type="text" ref={inputRef} />
        )
      }
    }
    
    class MyComponent extends Component {
      componentDidMount() {
        this.inputNode.focus();
      }
    
      render() {
        return (
          <div>
            Hello, <Field inputRef={node => this.inputNode = node} />
          </div>
        )
      }
    }
    

    【讨论】:

      【解决方案7】:

      这是我解决动态组件问题的方法:

      在父组件上,动态创建对子组件的引用,例如:

      class Form extends Component {
          fieldRefs: [];
      
          // dynamically create the child references on mount/init
          componentWillMount = () => {
              this.fieldRefs = [];
              for(let f of this.props.children) {
                  if (f && f.type.name == 'FormField') {
                      f.ref = createRef();
                      this.fieldRefs.push(f);
                  }
              }
          }
      
          // used later to retrieve values of the dynamic children refs
          public getFields = () => {
              let data = {};
      
              for(let r of this.fieldRefs) {
                  let f = r.ref.current;
                  data[f.props.id] = f.field.current.value;
              }
      
              return data;
          }
      }
      

      子组件(即 )实现它自己的“字段”引用,从父组件引用:

      class FormField extends Component {
          field = createRef();
          
          render() {
              return(
                  <input ref={this.field} type={type} />
              );
          }
      }
      

      然后在您的主页,“父的父”组件中,您可以从引用中获取字段值:

      class Page extends Component {
          form = createRef();
      
          onSubmit = () => {
              let fields = this.form.current.getFields();
          }
      
          render() {
              return (
                  <Form ref={this.form}>
                      <FormField id="email" type="email" autoComplete="email" label="E-mail" />
                      <FormField id="password" type="password" autoComplete="password" label="Password" />
      
                      <div class="button" onClick={this.onSubmit}>Submit</div>
                  </Form>
              );
          }
      }
      

      我实现这个是因为我想从一个主

      组件封装所有通用表单功能,并且能够让主客户端/页面组件设置并设置自己的内部组件样式的唯一方法是使用子组件组件(即父
      中的 项,它位于其他一些 组件中)。

      因此,虽然有些人可能认为这是一种 hack,但它就像 React 试图阻止任何父级的实际 'ref' 一样 hacky,我认为这是一个荒谬的设计,但是他们想要合理化它。

      【讨论】:

      • 考虑将 React 抨击移至答案的底部,而不是直接使用它。
      • 谢谢。我已经这样做了,甚至考虑删除它。然后,我忘记了。既然你提到了它,我想我会留下它,以便只有那些能够通读它、理解问题并仍然解决问题的人才能真正保证值得使用它。也许您对解决方案的完整性有一些 cmet?干杯。
      • 解决方案很好。这取决于您的意图,因为它是您的答案,但如果人们在这里专注于快速获得代码解决方案,那么您的领先优势就会阻止那个。
      • @CodeFinity 点已被采用并已编辑条目。谢谢。
      【解决方案8】:

      如果你所有的都是props.children:

      const Parent = (p: {children: JSX.Element}) => {
          const childRef = useRef()
      
          return React.cloneElement(p.children, { ref: childRef })
      }
      
      <Parent>
        <SingleChild />
      </Parent>
      

      请注意,如果您的孩子不能拥有ref,它将失败,例如React.Fragment.

      【讨论】: