通信机制
通信
常见的通信有哪几种形式?
父组件向子组件通信
子组件向父组件通信
跨级组件通信
兄弟组件通信
父组件向子组件通信
父组件向子组件传递 props,子组件得到 props 后进行相应的处理。
function Sup(props) {
const { user } = props;
return (
<p className="sup-context">
<span className="sup-context-title">Who am I, you are</span>: {user}
</p>
);
}
export default class Container extends Component {
constructor() {
super();
this.state = {
userName: 'shenxuxiang',
};
}
render() {
return (
<div className="page-one">
<h1 className="page-one-title">父组件向子组件通信</h1>
<Sup user={this.state.userName} />
</div>
);
}
}
Container 是一个父组件,Sup 作为子组件。我们将 user 属性传递到了 Sup 内部。这时我们就可以发现,在 Sup 内部是可以拿到 user 的值的。
子组件向父组件通信
利用回调函数,可以实现子组件向父组件通信: 父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可以向父组件通信。
export default class Sup extends PureComponent {
handleClick = (event) => {
const name = event.target.getAttribute("data-name");
// 这种直接修改 props 的形式会是页面报错
// this.props.user = name;
this.props.onChange(name);
};
render() {
return (
<Fragment>
<p className="page-two-sup-context">
<span className="page-two-sup-context-title">Who am I, you are</span>:{" "}
{this.props.user}
</p>
<ul onClick={this.handleClick} className="page-two-user-list">
<li className="page-one-user-list-item" data-name="小明">
小明
</li>
<li className="page-one-user-list-item" data-name="小强">
小强
</li>
<li className="page-one-user-list-item" data-name="小李">
小李
</li>
</ul>
</Fragment>
);
}
}
export default class Container extends Component {
constructor() {
super();
this.state = {
userName: "shenxuxiang",
};
}
handleChangeUserName = (name) => {
this.setState({ userName: name });
};
render() {
return (
<div className="page-two">
<h1 className="page-two-title">子组件向父组件通信</h1>
<Sup user={this.state.userName} onChange={this.handleChangeUserName} />
</div>
);
}
}
跨组件之间通信
跨组件通信,就是父组件向子组件的子组件通信,一般嵌套的层级超过三层我们就可以称为跨级。跨级组件通信可以采用下面两种方式:
中间组件层层传递 props
使用 context 对象
function Sup(props) {
return (
<div className="page-three-sup">
<div>这是 sup</div>
<Sub user={props.user} />
</div>
);
}
function Sub(props) {
return (
<div className="page-three-sub">
<div>这是 sub</div>
<div>{`props.user = ${props.user}`}</div>
</div>
);
}
class Container extends Component {
constructor() {
super();
this.state = {
userName: "shenxuxiang",
};
}
render() {
return (
<div className="page-three">
<h1 className="page-three-title">跨级组件通信</h1>
<Sup user={this.state.userName} />
</div>
);
}
}
使用 context 对象
使用 context 我们可以把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。
创建一个 context 对象
const ThemContext = React.createContext("skyblue");
必须使用 React.createContext 方法进行创建,参数可以是任意类型的值
Context.Provider
<ThemContext.Provider value={this.state.themColor}>
<Sup />
</ThemContext.Provider>
每个 Context 对象都会返回一个 Provider 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数。
const ThemContext = React.createContext("skyblue");
export default class Container extends Component {
constructor() {
super();
this.state = {
themColor: "#f80",
};
}
render() {
return (
<div className="page-three">
<h1 className="page-three-title">跨级组件通信</h1>
<ThemContext.Provider value={this.state.themColor}>
<Sup />
</ThemContext.Provider>
</div>
);
}
}
Container.contextType = ThemContext;
function Sup(props) {
return (
<div className="page-three-sup">
<div>这是 sup</div>
<Sub />
</div>
);
}
function Sub(props) {
const context = useContext(ThemContext);
return (
<div className="page-three-sub" style={{ background: context }}>
<div>这是 sub</div>
</div>
);
}
兄弟组件通信
兄弟组件通信,指的是在同一个父组件中的两个及以上的组件之间如何进行通信。针对这种情况,一般的做法就是将数据来源存放在它们共同的父级组件中。
export default class Container extends Component {
constructor() {
super();
this.state = {
userName: "shenxuxiang",
};
}
handleClick = (name) => {
this.setState({ userName: name });
};
render() {
const { userName } = this.state;
return (
<div className="page-four">
<h1 className="page-four-title">兄弟组件通信</h1>
<h2>userName={userName}</h2>
<Foo user={userName} onClick={this.handleClick} />
<Bar user={userName} onClick={this.handleClick} />
</div>
);
}
}
function Bar(props) {
return (
<div onClick={() => props.onClick("bar")}>
我是组件 Bar。{`【${props.user}】`}
</div>
);
}
function Foo(props) {
return (
<div onClick={() => props.onClick("foo")}>
我是组件 Foo。{`【${props.user}】`}
</div>
);
}
初始化时的参数传递
实例化阶段的方法调用
子组件向父组件传递消息
回调函数
class Child {
constructor(cb) {
// 调用父组件传入的回调函数, 发送消息
setTimeout(() => {
cb()
}, 2000);
}
}
class Parent {
constructor() {
//
初始化阶段, 传入回调函数
this.child = new Child(function() {
console.log(’child update’)
})
}
}
子组件部署消息接口
//event.js
class Event {
/** on 方法把订阅者所想要订阅的事件及相应的回调函数记录在 Event 对象的 _cbs 属性中*/
on(event, fn) {
if (typeof fn != "function") {
console.error("fn must be a function");
return;
}
this._cbs = this._cbs || {};
(this._cbs[event] = this._cbs[event] || []).push(fn);
}
/**emit 方法接受一个事件名称参数,在 Event 对象的 _cbs 属性中取出对应的数组,并逐个执行里面的回调函数 */
emit(event) {
this._cbs = this._cbs || {};
var callbacks = this._cbs[event],
args;
if (callbacks) {
callbacks = callbacks.slice(0);
args = [].slice.call(arguments, 1);
for (var i = 0, len = callbacks.length; i < len; i++) {
callbacks[i].apply(null, args);
}
}
}
/** off 方法接受事件名称和当初注册的回调函数作参数,在 Event 对象的 _cbs 属性中删除对应的回调函数。*/
off(event, fn) {
this._cbs = this._cbs || {};
// all
if (!arguments.length) {
this._cbs = {};
return;
}
var callbacks = this._cbs[event];
if (!callbacks) return;
// remove all handlers
if (arguments.length === 1) {
delete this._cbs[event];
return;
}
// remove specific handler
var cb;
for (var i = 0, len = callbacks.length; i < len; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
return;
}
}
const myEvent = new Event();
export default myEvent;
最后更新于
这有帮助吗?