React:使用refs从祖父组件读取子组件textarea的值
这个问题我之前也碰到过,核心原因是React的ref是组件局部作用域的——子组件里声明的ref="someref"只属于子组件自己,祖父组件的this.refs里根本不会有这个引用,所以你用ReactDOM.findDOMNode(this.refs.someref)返回null是完全正常的。
下面给你两种可行的解决方案,优先推荐第一种,更符合React的设计理念:
方案一:用受控组件(最推荐)
你的子组件里的textarea已经是受控组件了(用到了props.value和props.onChange),那我们只需要把状态提升到祖父组件,通过props层层传递,就能直接在祖父组件里拿到textarea的值,完全不需要依赖ref。
举个完整的例子:
祖父组件
class Grandparent extends React.Component { constructor(props) { super(props); // 把textarea的值存在祖父组件的state里 this.state = { textareaContent: '' }; this.handleTextChange = this.handleTextChange.bind(this); this.logTextValue = this.logTextValue.bind(this); } // 处理textarea的变化,更新state handleTextChange(e) { this.setState({ textareaContent: e.target.value }); } // 你需要的函数,直接从state里取值就行 logTextValue() { console.log('当前textarea的值:', this.state.textareaContent); } render() { return ( <div> {/* 把onChange和value传递给父组件 */} <Parent onChange={this.handleTextChange} value={this.state.textareaContent} /> <button onClick={this.logTextValue}>打印textarea值</button> </div> ); } }
父组件
父组件只需要把接收到的props透传给子组件就行:
class Parent extends React.Component { render() { return <Child onChange={this.props.onChange} value={this.props.value} />; } }
子组件
保持你原来的结构就行,不需要额外修改:
class Child extends React.Component { render() { return ( <textarea name={this.props.name} id={this.props.id} onChange={this.props.onChange} value={this.props.value} /> ); } }
这样一来,祖父组件的logTextValue函数直接访问this.state.textareaContent就能拿到textarea的实时值,数据流清晰,也更容易维护。
方案二:用Ref转发(适合非受控场景)
如果你确实需要通过ref直接获取textarea的DOM元素,可以用React.forwardRef把子组件的ref转发给祖父组件,不过这种方式更适合非受控组件的场景。
子组件(用forwardRef包裹)
把子组件改成函数组件并包裹在forwardRef里,把ref绑定到textarea上:
const Child = React.forwardRef((props, ref) => { return ( <textarea name={props.name} id={props.id} onChange={props.onChange} value={props.value} ref={ref} // 把外部传入的ref绑定到textarea /> ); });
父组件(透传ref)
父组件也需要用forwardRef来透传ref给子组件:
const Parent = React.forwardRef((props, ref) => { return <Child {...props} ref={ref} />; });
祖父组件
创建一个ref并传递给父组件,之后就能通过ref访问textarea元素:
class Grandparent extends React.Component { constructor(props) { super(props); // 创建一个ref对象 this.textareaRef = React.createRef(); this.logTextValue = this.logTextValue.bind(this); } logTextValue() { // 通过ref.current拿到textarea DOM元素 if (this.textareaRef.current) { console.log('当前textarea的值:', this.textareaRef.current.value); } } render() { return ( <div> <Parent ref={this.textareaRef} /> <button onClick={this.logTextValue}>打印textarea值</button> </div> ); } }
如果父组件是类组件,没办法直接接收forwardRef传递的ref,你可以把ref作为普通props传递(比如叫innerRef),然后在父组件里把props.innerRef传递给子组件。
内容的提问来源于stack exchange,提问作者user3808307

