React:将ref传递给道具的正确方法是什么?

Pie*_*ier 28 javascript reactjs

我正在尝试将组件的ref传递给另一个组件.由于字符串引用已被弃用,我正在使用回调引用.

所以我有类似的东西:

<One ref={c => this.one = c}/>
<Two one={this.one}/>
Run Code Online (Sandbox Code Playgroud)

问题是每当我尝试进入this.props.one内部时Two我都会得到undefined.

我甚至试过这个Two:

componentDidMount(){
    setTimeout(()=>{
        console.log(this.props.one);
    },5000)
}
Run Code Online (Sandbox Code Playgroud)

似乎问题在于,当创建prop时,ref仍然不存在,因为它已经创建了一次One.但我不知道如何"刷新"道具Two以获得已安装组件的引用.

那么将ref传递给另一个组件的正确方法是什么?

编辑

一些用户建议将该逻辑封装在更高的组件中,该组件本身会呈现其他子组件.

这种方法的问题在于您无法创建可重用的逻辑,并且必须在这些封装组件中反复重复相同的逻辑.

假设您要创建一个通用<Form>组件,它将提交逻辑封装到您的商店,错误检查等.您可以这样做:

<Form>
    <Input/>
    <Input/>
    <Input/>
    <Input/>
    <SubmitButton/> 
</Form>
Run Code Online (Sandbox Code Playgroud)

在此示例<Form>中,无法访问子节点的实例(和方法),因为this.props.children不返回这些实例.它返回一些伪组件列表.

那么如何在<Input/>不通过ref的情况下检查某个是否检测到验证错误?

您必须使用验证逻辑将这些组件封装在另一个组件中.例如在<UserForm>.但是,由于各形式是不同的相同的逻辑必须被复制在<CategoryForm>,<GoupForm>等等,这是非常低效的这就是为什么我要封装在验证逻辑<Form>和传递的引用<Input>部件<Form>.

Car*_*rre 22

通常,"ref"功能是React中的反模式.它的存在是为了实现副作用驱动的开发,但是为了从React的编程方式中获得最大的好处,你应尽量避免使用"refs".

至于你的特殊问题,给孩子一个参考它的兄弟是一个鸡与蛋的情景.挂载子进程时会触发ref回调,而不是在渲染期间触发,这就是为什么示例不起作用的原因.你可以尝试的一件事是将ref推入状态,然后从状态读到另一个孩子.所以:

<One ref={c => !this.state.one && this.setState({ one: c })}/>
<Two one={this.state.one}/>
Run Code Online (Sandbox Code Playgroud)

注意:如果没有!this.state.one这将导致无限循环.

下面是这个工作的codepen示例(查看控制台以查看sibling ref记录):http://codepen.io/anon/pen/pbqvRA


kev*_*oat 10

现在,使用新的ref api(从React 16开始可用-感谢perilandmishap指出了这一点),这要简单得多。

class MyComponent extends React.Component {
  constructor (props) {
    super(props);
    this.oneRef = React.createRef();
  }

  render () {
    return (
      <React.Fragment>
        <One ref={this.oneRef} />
        <Two one={this.oneRef} />
      </React.Fragment>
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您会Two像这样消耗道具:

class MyComponent extends React.Component {
  constructor (props) {
    super(props);
    this.oneRef = React.createRef();
  }

  render () {
    return (
      <React.Fragment>
        <One ref={this.oneRef} />
        <Two one={this.oneRef} />
      </React.Fragment>
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这种方法的一些注意事项:

引用将是具有current属性的对象。该属性将null 一直持续到元素/组件被安装为止。挂载后,它将是的实例One。一旦安装,应该安全地引用它<Two />

一旦<One />实例被卸载时,current在裁判回报是财产null

  • 仅当“&lt;One /&gt;”在“&lt;Two/&gt;”之前初始化时才有效。例如,如果您将 `&lt;Two/&gt;` 设置为 `&lt;One/&gt;` 的子级,那么 `Two` 将始终接收到 `null`,即使在它的 `componentDidMount` 内部也是如此。您需要以某种方式告诉“Two”“One”已安装。 (3认同)

Ben*_*her 5

一般来说,如果你需要传递一个在调用时可能没有设置的引用,你可以传递一个 lambda:

<One ref={c => this.one = c}/>
<Two one={() => this.one}/>
Run Code Online (Sandbox Code Playgroud)

然后将其引用为

this.props.one()
Run Code Online (Sandbox Code Playgroud)

如果在您调用它时已设置它,您将获得一个值。在此之前,你会得到undefined(假设它没有被初始化)。

值得注意的是,当它可用时你不一定会重新渲染,我希望它undefined在第一次渲染时出现。这是使用 state 来保存您的参考确实可以处理的事情,但您不会获得多次重新渲染。

鉴于这一切,我会建议任何代码用的是裁判的移动OneTwo成被渲染组件OneTwo,以避免所有这两种策略,和一个在@Carl斯维尔的答案的问题。