组件间通信
组件间通信
本部分我们暂时不考虑使用 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
组件中CardTextInput
与Card
还是需要显示的声明依赖关系。随着依赖复杂度的增加,我们不可避免的需要更多的构建弥合代码来进行依赖传递,也导致了大量的冗余的中间层组件。下面我们会使用Context
来改造上述代码,如果希望在组件中使用Context
,我们需要引入contextTypes
、getChildContext
、childContextTypes
这三个属性。首先对于父组件,我们将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
。