preface

Recently, I have been thinking about how to write React in the most comfortable way. From the tedious Redux to Context at the beginning, the data communication between components at the same level has gradually become simpler, but sometimes I also write redundant code, which makes me wonder again how to make the simple communication between components more pure

State of ascension

The recent project is doing background management, simple page often need such a situation, simple page is a query form, a table of results

const Page = (a)= > {
  return (
    <Container>
      <SearchForm />
      <ResultTable />
    </Container>
  );
};
Copy the code

The function is also very simple, query form input criteria, table display results, this is one-way communication of the component, that is, form criteria => query table, but because it is a peer component, have to state up

And when I go up, it looks like this

const Page = (a)= > {
  const [params,setParams] = useState();
  return (
    <Container>
      <SearchForm setParams={setParams}/>
      <ResultTable params={params}/>
    </Container>
  );
};
Copy the code

This works, but there is something extra. If you use data flow methods like Redux and Context, you don’t need to pass extra parameters, but there are more tedious steps

Triggered component refreshes

This name is a made-up one, but what I want to say is that

says

you have to re-request data, and

will re-request data


The first idea is that using the SWR library, the table component must request an interface. If the request is made using SWR, the table component can be refreshed using a trigger(‘/ API /tableList’) on any component, even if it is not in the component

So what about native, which is pretty simple, publish subscribe, rewrite components

const SearchForm = (a)= > {
  // Triggers the research event
  emitter.emit('research', { time: 'now' });
  // ...
};

const ResultTable = (a)= > {
  // When the research event is triggered
  emitter.on('research', ({ time }) => getList(time));
  // ...
};
Copy the code

This way, just like THE trigger component refresh I was thinking of, anywhere research is triggered, the results table will show the latest results

This approach is often used. Redux, for example, is implemented in a similar observer mode. Connect components subscribe to store updates, and each store update notifies the components to refresh and obtain new states

How does Emitter work

There are a lot of such libraries, WHAT I found is mitt written by the big guy of Preact, the source code is very short, also very simple, directly post code analysis

// The type of event
export type EventType = string | symbol;

// The Handler can receive an optional event argument, but does not return any value
export type Handler = (event? :any) = > void;
// Wildcard Handler
export type WildcardHandler= (type: EventType, event? :any) = > void

// The Handler array that is currently registered
export type EventHandlerList = Array<Handler>;
export type WildCardEventHandlerList = Array<WildcardHandler>;

// A map of event types. Each event type has a corresponding handler list
export type EventHandlerMap = Map<EventType, EventHandlerList | WildCardEventHandlerList>;

export interface Emitter {
    on(type: EventType, handler: Handler): void;
    on(type: The '*', handler: WildcardHandler): void;

    off(type: EventType, handler: Handler): void;
    off(type: The '*', handler: WildcardHandler): void;

    emit<T = any> (type: EventType, event? : T):void;
    emit(type: The '*', event? :any) :void;
}

/** Mitt: Tiny (~200b) functional event emitter / pubsub. * @name mitt * @returns {Mitt} */
export default function mitt(all? : EventHandlerMap) :Emitter {
    all = all || new Map();

    return {

        / * * * for a given event type register an event handler. * @ param {string | symbol} type to monitor the event type, The wildcard "*" listens for all functions * @param {Function} handlers respond to the given event * @memberof mitt */
        on(type: EventType, handler: Handler) {
            // Get the map handler array for type
            const handlers = all.get(type);
            // Add a new one if there is one
            const added = handlers && handlers.push(handler);
            // No new handler array is added
            if(! added) { all.set(type, [handler]); }},/ * * * removes a given event an event handler * @ param {string | symbol} type to remove the type of event handler, Or "*" * @param {Function} handler Removes the handler Function * @memberof mitt */
        off(type: EventType, handler: Handler) {
            const handlers = all.get(type);
            if (handlers) {
                // >>> 0 must be non-negative, the positive number stays the same, -1 becomes 4294967295
                handlers.splice(handlers.indexOf(handler) >>> 0.1); }},/** * Triggers all handler functions of the given event type * if the event type exists, the handler of "*" will be executed after all handlers of the matching type are executed ** Note: Manually trigger the "*" is not supported by the * * @ param {string | symbol} type need to call the event type * @ param {Any} (evt) Any type of value, recommend the object type, Is passed to each handler handler function * @memberof mitt */
        emit(type: EventType, evt: any) {
            / / slice a shallow copy, or remove https://github.com/developit/mitt/issues/65 there will be a problem
            // Map instead of foreach because 4 letters are missing...
            ((all.get(type) | | []) as EventHandlerList).slice(a).map((handler) => { handler(evt); });
            ((all.get(The '*') | | []) as WildCardEventHandlerList).slice(a).map((handler) => { handler(type, evt); }); }}; }Copy the code

The library is so simple that it’s only 200 bytes and has few features, but it’s just enough

Like Redux-Thunk, which only has a dozen lines of code but 14.9K Star, it’s all bullshit

conclusion

I feel that I have always followed the rules, so it is very old-fashioned, this small thought made me feel that there are many variable things, but also very convenient

However, I think pubsub should be properly used for fast communication and can be used as a quick backup option. I also remembered that I wrote FLUTTER a long time ago. Since I did not use any state management library, I chose pubsub library like event_bus

There are too many components passing information, so you still need to choose a good state management library. I recently found a good pullState library, so I have a chance to give it a try