组件间通信

组件间通信

本部分我们暂时不考虑使用 MobX 或者 Redux 进行状态管理

父组件向子组件通信

子组件向父组件通信

跨级组件通信

当我们需要让子组件跨级获取数据时,最简单暴力的方法就是多层嵌套,将信息从顶层组件一级一级的传递下来。不过这样的弊端也是显而易见,中间层的组件被迫处理大量自己本身不需要的数据。我们在前文中讨论过 Context 的概念与用法,虽然 Context 存在潜在问题,但是其涉及初衷就是用来解耦组件,保证数据的跨级传递。譬如我们在设计某个表单时,产品经理要求当用户点击某个输入框时,列表自动滚动到该输入框所在位置。首先,控制表单滚动的事件应该由表单组件处理,并且我们不希望输入组件与表单组件强耦合,因此我们没有在 Form 组件中显式声明输入组件,而是通过children属性动态传入:

// Parent Component
class Form extends Component {
  handleFocus = y => {
    this.scrollView.scrollTo({ y });
  };

  render() {
    const { children } = this.props;
    return (
      <ScrollView
        ref={r => {
          this.scrollView = r;
        }}
      >
        {children} 
      </ScrollView>
    );
  }
}

然后在具体的输入组件中,我们需要传入父组件的onFocus函数:

// Child Component
class FormTextInput extends Component {
  handleFocus = () => {
    if (!this.props.onFocus) return;
    this.props.onFocus(this.yPosition);
  };

  handleLayout = ({
    nativeEvent: {
      layout: { y }
    }
  }) => {
    this.yPosition = y;
  };

  render() {
    return (
      <TextInput onLayout={this.handleLayout} onFocus={this.handleFocus} />
    );
  }
}

最后我们需要某个连接组件,将这两个控件关联起来:

// A developer using your weak non-contextual component
class FormContainer extends Component {
  render() {
    return (
      <Form
        ref={r => {
          this.form = r;
        }}
      >
        <FormTextInput onFocus={this.form && this.form.handleFocus} />
      </Form>
    );
  }
}

这种方式在一定程度上解决了强耦合的问题,不过在Cards组件中CardTextInputCard还是需要显示的声明依赖关系。随着依赖复杂度的增加,我们不可避免的需要更多的构建弥合代码来进行依赖传递,也导致了大量的冗余的中间层组件。下面我们会使用Context来改造上述代码,如果希望在组件中使用Context,我们需要引入contextTypesgetChildContextchildContextTypes这三个属性。首先对于父组件,我们将handleFocus函数挂载到Context对象上:

// Parent Component
class Formextends Component {
  static childContextTypes = {
  handleFocus: PropTypes.func,
  }


  getChildContext = () => ({
  handleFocus: this.handleFocus,
  })


  handleFocus = (y) => {
  this.scrollView.scrollTo({ y });
  }


  render() {
  const {children} = this.props;
  return (
  <ScrollView ref={(r) => {this.scrollView = r}}>
  {children}
  </ScrollView>
  );
  }

}

然后在子组件中,我们显式声明依赖的父组件的Context结构:

// Child Component
class FormTextInput extends Component {
  static contextTypes = {
    handleFocus: PropTypes.func
  };

  handleFocus = () => {
    if (!this.context.handleFocus) return;
    this.context.handleFocus(this.yPosition);
  };

  handleLayout = ({
    nativeEvent: {
      layout: { y }
    }
  }) => {
    this.yPosition = y;
  };

  render() {
    return (
      <TextInput onLayout={this.handleLayout} onFocus={this.handleFocus} />
    );
  }
}

最后在使用这两个组件的地方,我们不需要再以ref进行句柄传递,而是直接嵌套即可:

// A developer using your awesome it-just-works component
class FormContainer extends Component {
  render() {
    return (
      <Form>
        <FormTextInput />
      </Form>
    );
  }
}

不过这里还是需要强调的是,Context的改变并不会触发props或者state的改变,因此也无法触发组件发生重渲染,并且大量使用Context的情况下也会导致依赖混乱,因此我们在真实的项目中还是需要慎用Context

无嵌套关系组件通信

上一页