Skip to content

Commit

Permalink
fix: Connect component supports to unsubscribe event by pass the call…
Browse files Browse the repository at this point in the history
…back (#635)

* feat: eventEmitter supports to unsubscribe event by pass the callback

* test: unsubscribe the event by pass the callback

* test: unsubscribe the event by pass the callback

* fix: set the height 100% while in horizontal mode

* fix: rename to uppercase

* feat: add use case for react/connect function

* test: new testService instance

* refactor: rename callback to listener
  • Loading branch information
wewoor authored Jan 20, 2022
1 parent b352afd commit bde0274
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 40 deletions.
23 changes: 23 additions & 0 deletions src/common/event/__tests__/eventEmitter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,27 @@ describe('Test the EventEmitter class', () => {
event.unsubscribe(['a', 'b']);
expect(event.count('a')).toBe(0);
});

test('Unsubscribe the event by pass the callback', () => {
const evt = new EventEmitter();
const eventName = 'event1';
const mockFn1 = jest.fn();
const mockFn2 = jest.fn();
evt.subscribe(eventName, mockFn1);
evt.subscribe(eventName, mockFn2);

expect(evt.count(eventName)).toBe(2);

evt.unsubscribe(eventName, mockFn1);
expect(evt.count(eventName)).toBe(1);
evt.emit(eventName);
expect(mockFn1).toBeCalledTimes(0);
expect(mockFn2).toBeCalledTimes(1);

evt.subscribe(eventName, mockFn1);
expect(evt.count(eventName)).toBe(2);

evt.unsubscribe(eventName);
expect(evt.count(eventName)).toBe(0);
});
});
14 changes: 7 additions & 7 deletions src/common/event/eventBus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export abstract class GlobalEvent {
/**
* Subscribe the service event
* @param name Event name
* @param callback Callback function
* @param listener Listener function
*/
public subscribe(name: string | string[], callback: Function) {
EventBus.subscribe(name, callback);
public subscribe(name: string | string[], listener: Function) {
EventBus.subscribe(name, listener);
}

/**
Expand All @@ -30,11 +30,11 @@ export abstract class GlobalEvent {
}

/**
* Unsubscribe the specific event
* Unsubscribe the specific event and the listener function
* @param name The event name
* @param callback The subscribed function
* @param listener optional, it unsubscribes events via name if not pass the listener function
*/
public unsubscribe(name) {
EventBus.unsubscribe(name);
public unsubscribe(name, listener?: Function) {
EventBus.unsubscribe(name, listener);
}
}
35 changes: 19 additions & 16 deletions src/common/event/eventEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,43 @@ export class EventEmitter {
}
}

public subscribe(name: string | string[], callback: Function) {
public subscribe(name: string | string[], listener: Function) {
if (Array.isArray(name)) {
name.forEach((key: string) => {
this.assignEvent(key, callback);
this.assignEvent(key, listener);
});
} else {
this.assignEvent(name, callback);
this.assignEvent(name, listener);
}
}

/**
* Unsubscribe the specific event by the name
*
* TODO: The `unsubscribe` method delete the all events via the name directly, the developer
* use the `subscribe` method could register many callbacks, so if the developer only want to delete the specific callback by the name,
* this method is no work.
* @param name The removed event name
*/
public unsubscribe(name: string | string[]) {
public unsubscribe(name: string | string[], listener?: Function) {
if (Array.isArray(name)) {
name.forEach((key: string) => {
this._events.delete(key);
this.deleteEvent(key, listener);
});
} else {
this.deleteEvent(name, listener);
}
}

public deleteEvent(name: string, listener?: Function) {
if (listener) {
const event = this._events.get(name);
if (event) {
event.splice(event.indexOf(listener), 1);
}
} else {
this._events.delete(name);
}
}

public assignEvent<T>(name: string, callback: Function) {
public assignEvent<T>(name: string, listener: Function) {
const event = this._events.get(name);
if (event) {
event.push(callback);
event.push(listener);
} else {
this._events.set(name, [callback]);
this._events.set(name, [listener]);
}
}
}
1 change: 1 addition & 0 deletions src/components/menu/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

&--horizontal {
flex-direction: row;
height: 100%;
}

&__item {
Expand Down
25 changes: 24 additions & 1 deletion src/react/__tests__/connector.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Component, connect } from 'mo/react';
import { Component, ComponentEvents, connect } from 'mo/react';
import { fireEvent, render } from '@testing-library/react';

class TestServiceA extends Component {
Expand Down Expand Up @@ -122,4 +122,27 @@ describe('Test Connector Component', () => {

expect(serviceA.removeOnUpdateState).toBeCalled();
});

test('The Service connect multiple Components', () => {
const testService = new TestServiceA();

const TestView = connect({ A: testService }, TestComponent);
const TestView2 = connect({ A: testService }, TestComponent);

const { unmount } = render(<TestView />);
const { unmount: unmount2 } = render(<TestView2 />);
expect((testService as any)._event.count(ComponentEvents.Update)).toBe(
2
);

unmount();
expect((testService as any)._event.count(ComponentEvents.Update)).toBe(
1
);

unmount2();
expect((testService as any)._event.count(ComponentEvents.Update)).toBe(
0
);
});
});
17 changes: 9 additions & 8 deletions src/react/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export interface IComponent<S = any> {
render(nextState?: S): void;
/**
* Listen to the Component state update event
* @param callback
* @param listener
*/
onUpdateState(callback: (prevState: S, nextState: S) => void): void;
onUpdateState(listener: (prevState: S, nextState: S) => void): void;
/**
* Remove the Component update event listening
* Remove the Component update event listening, default is remove all,
* also you can remove one by pass the listener
*/
removeOnUpdateState(): void;
removeOnUpdateState(listener?: Function): void;
/**
* Force to update the Component
*/
Expand Down Expand Up @@ -68,12 +69,12 @@ export abstract class Component<S = any>
this._event.emit(ComponentEvents.Update, this.state, nextState);
}

public onUpdateState(callback: (prevState: S, nextState: S) => void) {
this._event.subscribe(ComponentEvents.Update, callback);
public onUpdateState(listener: (prevState: S, nextState: S) => void) {
this._event.subscribe(ComponentEvents.Update, listener);
}

public removeOnUpdateState(): void {
this._event.unsubscribe(ComponentEvents.Update);
public removeOnUpdateState(listener?: Function): void {
this._event.unsubscribe(ComponentEvents.Update, listener);
}

public forceUpdate() {
Expand Down
8 changes: 1 addition & 7 deletions src/react/connector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,10 @@ export function connect<T = any>(
componentWillUnmount() {
this._isMounted = false;
this.handleService((service) => {
service.removeOnUpdateState();
service.removeOnUpdateState(this.onChange);
});
}

// TODO: 目前会全量触发更新,后期根据字段(watchField)来控制更新粒度
// const prev = get(prevState, watchFiled);
// const next = get(nextState, watchFiled);
// if (!equals(prev, next)) {
// this.update();
// }
onChange(prevState, nextState) {
Logger.info(prevState, nextState, (container as any)._registry);
this.update();
Expand Down
2 changes: 1 addition & 1 deletion stories/components/17-Dialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { storiesOf } from '@storybook/react';
import { withKnobs } from '@storybook/addon-knobs';

const confirm = Modal.confirm;
const stories = storiesOf('dialog', module);
const stories = storiesOf('Dialog', module);
stories.addDecorator(withKnobs);

stories.add('Basic Usage', () => {
Expand Down
21 changes: 21 additions & 0 deletions stories/extensions/extend-panel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { IExtensionService } from 'mo/services';
import { IExtension } from 'mo/model';
import molecule from 'mo';

import { Pane } from './pane';

export const ExtendPanel: IExtension = {
id: 'ExtendsProblems',
name: 'Extends Problems',
activate(extensionCtx: IExtensionService) {
molecule.panel.add({
id: 'TestPanel',
name: 'Test Panel',
renderPane: () => <Pane />,
});
},
dispose() {
molecule.problems.remove(1);
},
};
9 changes: 9 additions & 0 deletions stories/extensions/extend-panel/pane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import molecule from 'mo';
import { IEditor } from 'mo/model';
import { connect } from 'mo/react';
import React from 'react';

export const Pane = connect(molecule.editor, function ({ current }: IEditor) {
const value: string = current?.tab?.data?.value || '!!!';
return <div style={{ padding: 20 }}>Editor Input: {value}</div>;
});
3 changes: 3 additions & 0 deletions stories/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { ExtendsLocalesPlus } from './locales-plus';

import { ExtendsTestPane } from './test';

import { ExtendPanel } from './extend-panel';

export const customExtensions: IExtension[] = [
ExtendsDataSync,
ExtendsTestPane,
ExtendsProblems,
ExtendPanel,
ExtendsLocalesPlus,
];

0 comments on commit bde0274

Please sign in to comment.