I. Observer model

Summary: Need to understand the relationship between the target and the observer

  • The relationship between target and observer is similar to that between teacher and student. The teacher has a cram school, students have to go to the teacher to listen to the teacher, the teacher began to teach.
  • The students understood the teacher’s lesson and updated their knowledge. The teacher has a direct relationship with the students, a face-to-face relationship between them.

Conclusion:

  • Observer mode is mostly used within a single application without a third party to implement scheduling. Defines a one-to-many relationship between objects.

Implementation approach

  • A) Subject B) Observer C) Subject D) Subject

code

// util.js
   export const Subject = function(eventName = 'default') {
       this.observerList = [];
       this.eventName = eventName;
       this._t;
   }
   
   // Register the observer to the target instance, which can be read as observer -> Subject;
   Subject.prototype.add = function(observer) {
       // observerList many students
       if(Array.isArray(observer)) {
           this.observerList.push(... observer); }else {
           this.observerList.push(observer); }}// Remove the observer as an observer;
   Subject.prototype.remove = function(observer) {
       const index = this.observerList.findIndex((item) = >item === observer);
       if(index ! = = -1) { 
           this.observerList.splice(index,1);
           return true;
       } else {
           return false; }}// Notify all students of the content (observerList)
   Subject.prototype.notify = function(data){
       // Anti-shake processing
       clearTimeout(this._t);
       this._t = setTimeout(() = > {
           const observerList = this.observerList;
           for(let i=0,len=observerList.length; i < len; i++) {
                // Update is an interface exposed by the Observer and cannot be notified if it is not provided
                observerList[i].update({eventName: this.eventName, data})
           }
       }, 500); 
       // This is set to 500 to test the effect, usually set to 50

   }
   export const Observer = function(fn) {
       const _fn = fn;
       this.update = function(data) {
           _fn(data)
       }
   }
   
Copy the code

The actual usage

Import * as React from 'React'; import { Subject, Observer } from './util'; const subject = new Subject(); export default class ClassRoom extends React.Component { constructor(props) { super(props); this.student = new Observer(this.callback); this.student2 = new Observer(this.callback2); subject.add([this.student,this.student2]); console.log(subject); } componentWillUnmount() { console.log(subject.remove(this.student)); console.log(subject.remove(this.student2)); console.log(subject); } callback = ({eventName, data}) => { console.log('student',data); }; callback2 = ({eventName, data}) => { console.log('student',data); } teacher = () => {subject. Notify (' subject... '); }; render() { return <button onClick={this.teacher}>notify</button> } }Copy the code

Publish and subscribe mode

Summary: Need to understand the relationship between dispatch center, subscriber, and publisher.

  • Luo Xiang opened a column on the Internet. The teacher uploaded the class to the platform. Students who like the class subscribe to the column. Later, the teacher only needs to upload the video to the platform, and the platform will push the updated video to subscribers.
  • The connection between the teacher and the student is not so big, and the two are connected through the platform.

conclusion

  • The publish-subscribe model is a variation of the observer model, in which publishers and subscribers are unaware of each other’s existence and are connected to each other through a dispatch center. The name of the event has always been a condition for them to connect with each other.
  • It is mainly used to flaten the multi-layer transparent communication mode.

Implementation approach

  • It’s basically implementing register, Watcher, trigger, unregister which is a method. This is the order of the whole process.
  • Watcher contains register and unregister.

Code (Regular version)

// util.js
export const pubsubFactory = function (myPubsub) {
  let eventCenter = {};
  let eventTimeout = {};
  
  myPubsub.getEventCenter = function () {
    return eventCenter;
  };
  
  myPubsub.watcher = function (eventName, fn) {
    // If there is no register, register the event name and add fn to the queue
    if(! eventCenter[eventName]) { eventCenter[eventName] = [fn]; }else {
      eventCenter[eventName].push(fn);
    }
    // unregister
    return function () {
      let events = eventCenter[eventName];
      eventCenter[eventName] = events.filter((handler) = >handler ! == fn); }; }; myPubsub.trigger =function (eventName, data) {
    const events = eventCenter[eventName];
    if(! events) {console.log(eventName + 'Event not registered');
      return;
    }
    let _t = eventTimeout[eventName];
    clearTimeout(_t);
    eventTimeout[eventName] = setTimeout(() = > {
      for (let i = 0, len = events.length; i < len; i++) { events[i]({ eventName, data }); }},50);
  };
  return myPubsub;
};

export const pubsub = pubsubFactory({});

Copy the code

The actual usage

// Actual usage
import * as React from 'React';
import { pubsub } from './util';

export default class PubSubDemo extends React.Component {
  constructor(props) {
    super(props);
    this.subject = pubsub.watcher('event'.this.callback);
    this.subject2 = pubsub.watcher('event2'.this.callback2);
  }

  componentWillUnmount() {
    this.subject();
    this.subject2();
    console.log(pubsub.getEventCenter())
  }

  callback = ({ eventName, data }) = > {
    console.log(eventName, data);
  };

  callback2 = ({ eventName, data }) = > {
    console.log(eventName, data);
  };

  publish = () = > {
    pubsub.trigger('event'.'data1');
  };

  publish2 = () = > {
    pubsub.trigger('event2'.'data2');
  };

  render() {
    return (
      <div>
        <button onClick={this.publish}>publish</button>
        <button onClick={this.publish2}>publish2</button>
      </div>); }}Copy the code

Code (Decorator version)

// util.js
// This version does not support hook components
import * as _ from "lodash";
function decorator() {
  const events = [];
  let cache = undefined;
  let _t = null;

  const register = callback= > {
    if (!events.includes(callback)) {
      events.push(callback);
    }
  };

  const unregister = callback= > {
    const index = events.indexOf(callback);
    events.splice(index, 1);
    return index > -1;
  };

  const trigger = value= > {
      clearTimeout(_t);
      cache = value;
      _t = setTimeout(() = > {
        events.forEach(callback= > {
          _.isFunction(callback) && callback(cache);
        });
      }, 50);
  };

  const watcher = function(inst, funcName) {
    const name = ` __${funcName}__ `;
    const { componentDidMount, componentWillUnmount } = inst;
    // There is a problem with multilevel recursive calls and it is not recommended to bind too many PubSub events to one component.
    inst.componentDidMount = function() {
      this[name] = this[funcName].bind(this);
      register(this[name]);
      if (_.isFunction(componentDidMount)) {
        componentDidMount.call(this); }}; inst.componentWillUnmount =function() {
      unregister(this[name]);
      delete this[name];
      if (_.isFunction(componentWillUnmount)) {
        componentWillUnmount.call(this); }}; };return {
    watcher,
    trigger,
    register,
    unregister
  };
}

const DF = {};
export default function decoratorFactory(name = "default") {
  if(! (namein DF)) {
    DF[name] = decorator();
  }
  return DF[name];
}
Copy the code

The actual usage

// Actual usage
import * as React from 'React';
import decoratorFactory from './util';

const state = decoratorFactory('event');
const state2 = decoratorFactory('event2');

export default class DecoratorPubsubDemo extends React.Component {

  componentWillUnmount() {
    console.log(state);
    console.log(state2);
  }
  @state.watcher
  callback = (data) = > {
    console.log(data);
  };
  @state2.watcher
  callback2 = (data) = > {
    console.log(data);
  };

  publish = () = > {
    state.trigger('data1');
  };

  publish2 = () = > {
    state2.trigger('data2');
  };

  render() {
    return (
      <div>
        <button onClick={this.publish}>publish</button>
        <button onClick={this.publish2}>publish2</button>
      </div>); }}Copy the code

Third, refer to the article

  • Design patterns
  • Ruan Yifeng, the use of decorators to achieve automatic publishing events

Four, conclusion

The above views are just personal understanding of the output of practice, welcome you to supplement the big guy passing by