通信机制

通信

常见的通信有哪几种形式?

  1. 父组件向子组件通信

  2. 子组件向父组件通信

  3. 跨级组件通信

  4. 兄弟组件通信

父组件向子组件通信

父组件向子组件传递 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;

最后更新于

这有帮助吗?