组件的复用是 React 中非常重要的一个设计,使得界面布局的重用十分便捷,我们可以根据自己的需要来自定义各种组件,这对于结构相似的 UI 构建是很友好的。
所以经常我们会出现如下的代码结构(其中的 MyComponent 是自定义组件):
export default class Home extends Component{
...
render(){
<View>
<MyComponent/>
</View>
}
}
在这种结构中,Home 组件包含了 MyComponent 组件,形成父子组件关系,其中自然是少不了有数据的传递,甚至会需要调用互相的函数,那么父子组件之间是如何传值和函数调用的呢?
子组件获取父组件的变量和函数
如果我们需要在 MyComponent 中获取到 Home 页面中的数据的话,可以使用通过 props 来传递。需要注意的是传递的数据,可以是普通的变量,也可以是函数。如例:
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
buttonText: '点击调用父组件 Home 的函数',
}
}
onMyButtonPress = () => { //父组件的方法
ShowToast('Home 中的方法');
}
render() {
return (
<View>
<MyButton
//传递 text 变量
text={this.state.buttonText}
//传递 onMyButtonPress 作为 子组件的 onPress 函数
onPress={this.onMyButtonPress}
/>
</View>
);
}
}
class MyButton extends Component {
render() {
//获取传递的 onPress 函数和 text 变量
const {text, onPress} = this.props;
return (
<TouchableOpacity
style={styles.button}
//实际调用的是父组件传过来的 onPress 函数
onPress={onPress}
>
<Text>{text}</Text>
</TouchableOpacity>
);
}
}
(代码重点表现 props 传递,省略了导入和样式部分)
效果:

上部分的 MyButton, 按钮中的文字和点击事件都是外部父组件传入的,本身不持有任何静态变量。在此基础上,我们可以加上自定义的样式或者其他元素,就成为一个可以复用的按钮组件。
父组件获取子组件的变量和函数
如果我们要在外部的父组件中获取到子组件的值的话,那就需要使用到 ref 了。如果对 ref 不太熟,可以参见 [Refs and the DOM] 。
这里,你可以简单理解为绑定 ref 就可以获取到当前的 DOM 元素节点,从而获取它的变量和函数。
怎么操作呢?如下:
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
buttonText: '点击调用父组件 Home 的函数',
}
//创建 MytextInput 的 ref
this.myButton = React.createRef();
}
//输出子组件中的变量,调用子组件方法
onMyButtonPress = () => {
//在调试控制台输出 输入框中输入的值
console.log('输入的值:' + this.mytextInput.current.state.inputText);
//调用 MyTextInput 内部的方法 innerMethod
this.mytextInput.current.innerMethod();
//将 MyTextInput 输入框的内容清除掉 (clear是TextInput组件本身的方法)
this.mytextInput.current.textInputRef.current.clear();
}
render() {
return (
<View>
<MyTextInput
ref={this.mytextInput}
/>
<TouchableOpacity
onPress={this.onMyButtonPress}
>
<Text>确认</Text>
</TouchableOpacity>
</View>
);
}
}
//自定义组件 MyTextInput
class MyTextInput extends Component {
constructor(props) {
super(props);
this.state = {
inputText: ''
}
this.textInputRef = React.createRef();
}
// MyTextInput 的普通方法
innerMethod = () => {
ShowToast('MyTextInput 内部方法');
}
render() {
return (
<View>
<TextInput
placeholder={'输入内容后点击按钮'}
style={styles.input}
onChangeText={text => this.setState({inputText: text})}
ref={this.textInputRef}
/>
</View>
);
}
}
效果:


可见,子组件中的 TextInput 的输入内容,可以在父组件中获取并打印输出出来, innerMethod 是定义在子组件中的函数,也可以在父组件中得以调用。而子组件的 TextInput 组件的 clear 方法(清除输入框的内容),依然能被父组件正确调用。
至此,我们已经在父组件中获取到子组件的变量和函数了。
回调函数
如果只是需要传递数值到父组件,不需要在父组件中调用子组件的方法,并且触发的动作事件是在子组件中发生的话(比如子组件点击之后需要将一个变量暴露到父组件中,在父组件的多个地方用于显示),那可以考虑使用回调函数。
例如:在子组件中有一个输入框,输入完成后,点击软键盘的完成提交,暴露给父组件。
子组件中:
//调用父组件的函数,传入值
onSubmitPress = () => {
const {submitText} = this.props;
//调用父组件传过来的函数,传入 值(输入的文本)
submitText(this.state.inputText);
}
render() {
return (
<View>
<TextInput
onSubmitEditing={this.onSubmitPress}
...
/>
</View>
);
}
父组件中使用:
<MyTextInput
//接收传过来的值, setState
submitText={text => this.setState({resultText: text})}
ref={this.myButton}
/>
这样,我们在输入完成后,提交方法,调用父组件传过来的 submitText 函数,并传入的输入的文本,父组件的方法接收到再 setState 就获得了这个变量。至此,父组件就获取到了子组件中的变量。
在父组件传值到子组件的过程中,如果层级较深,应该考虑使用状态管理工具。子组件传值到父组件,除了以上方法,还可以通过 useRef,useImperativeHandle 等 Hook API 达到访问子组件数据的目的。这将在后面的 Hook 相关章节介绍,敬请期待。