[React Native]基础-父子组件之间的传值和函数调用

3,093 阅读4分钟

组件的复用是 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 相关章节介绍,敬请期待。