我正在尝试将组件的引用传递给另一个组件。由于risky practice我正在使用回调引用。
所以我有类似的东西:
<One ref={c => this.one = c}/>
<Two one={this.one}/>
问题在于,每当我尝试访问this.props.one
内的Two
时,我都会undefined
。
我甚至在Two
上尝试了这个:
componentDidMount(){
setTimeout(()=>{
console.log(this.props.one);
},5000)
}
似乎问题在于,当创建prop时,ref仍然不存在,因为它是在One
安装后创建的。但我不知道如何“刷新”Two
上的道具以获得已安装组件的参考。
那么将ref传递给另一个组件的正确方法是什么?
修改
有些用户建议将该逻辑封装在更高的组件中,该组件本身会呈现其他子组件。
该方法的问题在于您无法创建可重用的逻辑,并且必须在这些封装组件中反复重复相同的逻辑。
假设您要创建一个通用的<Form>
组件,它将提交逻辑封装到您的商店,错误检查等。您可以执行以下操作:
<Form>
<Input/>
<Input/>
<Input/>
<Input/>
<SubmitButton/>
</Form>
在此示例中,<Form>
无法访问子节点的实例(和方法),因为this.props.children
不返回这些实例。它返回一些伪组件列表。
那么如何在不传递参考的情况下检查某个<Input/>
是否检测到验证错误?
您必须使用验证逻辑将这些组件封装在另一个组件中。例如在<UserForm>
中。但由于每个表单都不同,因此必须在<CategoryForm>
,<GoupForm>
等中复制相同的逻辑。这非常低效,这就是为什么我要在<Form>
中封装验证逻辑并通过<Input>
组件<Form>
的引用。
答案 0 :(得分:21)
通常,“ref”功能是React中的反模式。它的存在是为了实现副作用驱动的开发,但是为了从React的编程方式中获得最大的好处,你应尽量避免使用“refs”。
至于你的特殊问题,给一个孩子传递一个参考它的兄弟是一个鸡与蛋的情景。挂载子进程时会触发ref回调,而不是在渲染期间触发,这就是为什么示例不起作用的原因。你可以尝试的一件事是将ref推入状态,然后从状态读到另一个孩子。所以:
<One ref={c => !this.state.one && this.setState({ one: c })}/>
<Two one={this.state.one}/>
注意:如果没有!this.state.one
,这将导致无限循环。
以下是此工作的codepen示例(查看控制台以查看记录的兄弟参考):http://codepen.io/anon/pen/pbqvRA
答案 1 :(得分:2)
现在使用new 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>
}
}
}
您会在Two
中消耗道具,例如:
this.props.one.current
此方法的一些注意事项:
该引用将是具有current
属性的对象。该属性将null
直到元素/组件被安装。挂载后,它将是One
的实例。装入<Two />
后,应该安全地引用它。
一旦卸载了<One />
实例,则引用上的current
属性将恢复为null
。
答案 2 :(得分:1)
通常,如果您需要传递对调用时未设置的内容的引用,则可以传递lambda代替:
<One ref={c => this.one = c}/>
<Two one={() => this.one}/>
,然后将其引用为
this.props.one()
如果在调用它时已设置它,则会得到一个值。在此之前,您将获得undefined
(假设尚未初始化)。
需要注意的是,当它可用时,您不一定会重新渲染,我希望它是第一个渲染器上的undefined
。使用状态来保存引用确实可以解决此问题,但是您不会获得多次重新渲染。
鉴于所有这些,我建议将One
中使用对Two
的引用的所有代码移到呈现One
和Two
的组件中,以避免所有与该策略有关的问题,以及@Carl Sverre的答案中的一个问题。