Skip to content

Commit cd910cd

Browse files
authored
Merge pull request aws-amplify#544 from aws-amplify/undefobj-patch-3
Docs for new Hub refactor
2 parents d2f16b8 + 5618d97 commit cd910cd

File tree

1 file changed

+186
-33
lines changed

1 file changed

+186
-33
lines changed

js/hub.md

Lines changed: 186 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
---
33
# Hub
44

5-
AWS Amplify has a local event bus system called Hub. It is a lightweight implementation of Publisher-Subscriber pattern, and is used to share data between modules and components in your app.
5+
Amplify has a local eventing system called Hub. It is a lightweight implementation of Publisher-Subscriber pattern, and is used to share data between modules and components in your app. Amplify uses Hub for different categories to communicate with one another when specific events occur, such as authentication events like a user sign-in or notification of a file download.
66

77
## Installation
8-
9-
Import:
108
```javascript
119
import { Hub } from 'aws-amplify';
1210

@@ -16,30 +14,20 @@ import { Hub } from '@aws-amplify/core';
1614

1715
## Working with the API
1816

19-
### dispatch()
20-
21-
You can dispatch an event with `dispatch` function:
22-
```javascript
23-
Hub.dispatch('auth', { event: 'signIn', data: user }, 'Auth');
24-
```
17+
### Listening for messages
2518

26-
### listen()
19+
`Hub.listen(channel: string | RegExp, callback)` is used to listen for messages that have been dispatched. You must provide either a named `channel` or a regular expression, along with a callback. In the case of a regular expression only dispatches which contain a `message` in their payload will be matched against your pattern. You can add multiple listeners to your application for different channels or patterns to listen for, or trap generic events and perform your own filtering.
2720

28-
You can subscribe to a channel with `listen` function:
2921
```javascript
30-
import { Hub, Logger } from 'aws-amplify';
31-
32-
const logger = new Logger('MyClass');
22+
import { Hub } from 'aws-amplify';
3323

3424
class MyClass {
3525
constructor() {
36-
Hub.listen('auth', this, 'MyListener');
37-
}
38-
39-
// Default handler for listening events
40-
onHubCapsule(capsule) {
41-
const { channel, payload } = capsule;
42-
if (channel === 'auth') { this.onAuthEvent(payload); }
26+
Hub.listen('auth', (data) => {
27+
const { payload } = data;
28+
this.onAuthEvent(payload);
29+
console.log('A new auth event has happened: ', data.payload.data.username + ' has ' + data.payload.event);
30+
})
4331
}
4432

4533
onAuthEvent(payload) {
@@ -48,43 +36,208 @@ class MyClass {
4836
}
4937
```
5038

51-
In order to capture event updates, you need to implement `onHubCapsule` handler function in your listener class.
39+
**A note about onHubCapsule** In previous versions of Amplify capturing updates required you to implement an `onHubCapsule` handler function in your class and pass in `this` to the listen method. While still possible, this is no longer considered best practice and we have begun deprecating the method. Please define an explicit callback and pass it into the listen function (e.g. `Hub.listen('auth', this.myCallback)`) or use an anonymous function such as in the above example.
5240
{: .callout .callout--info}
5341

54-
### Listening Authentication Events
42+
### Sending messages
43+
44+
Sending events to different channels is done with the `dispatch` function:
45+
```javascript
46+
Hub.dispatch(
47+
'DogsChannel',
48+
{
49+
event: 'buttonClick',
50+
data: {color:'blue'},
51+
message:''
52+
});
53+
54+
setTimeout(() => {
55+
Hub.dispatch(
56+
'CatsChannel',
57+
{
58+
event: 'drinkMilk',
59+
data: {
60+
breed: 'Persian',
61+
age: 5
62+
},
63+
message: `The cat ${cat.name} has finished her milk`
64+
});
65+
}, 5000)
66+
```
67+
`Hub.dispatch(channel: string, payload: HubPayload)` can be used to dispatch a `HubPayload` to a `channel`. The `channel` is a logical grouping for your organization while the `HubPayload` is a type defined as:
68+
```javascript
69+
export type HubPayload = {
70+
event: string,
71+
data?: any,
72+
message?: string
73+
};
74+
```
5575

56-
AWS Amplify Authentication module publishes in `auth` channel when 'signIn', 'signUp', and 'signOut' events happen. You can create your listener to listen and act upon those event notifications.
76+
The `event` field is recommended to be a small string without spaces such as `signIn` or `hang_up` as it's useful for checking payload groupings. The `data` field is a freeform structure which many times is used for larger JSON objects or custom data types. Finally while `message` is optional, we encourage you to use it as it is required when using a `RegExp` filtering with `Hub.listen()`.
77+
78+
### Stop Listening
79+
80+
Hub provides a way to stop listening for messages with `Hub.remove(channel: string | RegExp, listener: callback)`. This may be useful if you no longer need to receive messages in your application flow, as well as to avoid any memory leaks on low powered devices when you are sending large amounts of data through Hub on multiple channels.
81+
82+
83+
### Channels
84+
A channel is a logical group name that you use to organize messages and listen on. These are strings and completely up to you as the developer to define for dispatching or listening. However, while you can dispatch to any channel, ***Amplify protects certain channels*** and will flag a warning as sending unexpected payloads could have undesirable side effects (such as impacting authentication flows). The protected channels are currently:
85+
86+
- core
87+
- auth
88+
- api
89+
- analytics
90+
- interactions
91+
- pubsub
92+
- storage
93+
- xr
94+
{: .callout .callout--info}
95+
96+
97+
### Authentication Events
98+
99+
Amplify's `Auth` category publishes in the `auth` channel when 'signIn', 'signUp', and 'signOut' events happen. You can listen and act upon those event notifications.
57100

58101
```javascript
59102
import { Hub, Logger } from 'aws-amplify';
60103

61-
const alex = new Logger('Alexander_the_auth_watcher');
104+
const logger = new Logger('My-Logger');
62105

63-
alex.onHubCapsule = (capsule) => {
106+
const listener = (data) => {
64107

65-
switch (capsule.payload.event) {
108+
switch (data.payload.event) {
66109

67110
case 'signIn':
68-
alex.error('user signed in'); //[ERROR] Alexander_the_auth_watcher - user signed in
111+
logger.error('user signed in'); //[ERROR] My-Logger - user signed in
69112
break;
70113
case 'signUp':
71-
alex.error('user signed up');
114+
logger.error('user signed up');
72115
break;
73116
case 'signOut':
74-
alex.error('user signed out');
117+
logger.error('user signed out');
75118
break;
76119
case 'signIn_failure':
77-
alex.error('user sign in failed');
120+
logger.error('user sign in failed');
78121
break;
79122
case 'configured':
80-
alex.error('the Auth module is configured');
123+
logger.error('the Auth module is configured');
81124

82125
}
83126
}
84127

85-
Hub.listen('auth', alex);
128+
Hub.listen('auth', listener);
86129
```
87130

131+
### Listening for Regular Expressions
132+
133+
The listener feature of Hub is a powerful way to perform filtering when you're unsure what the data across different channels will look like. Additionally it's also a nice realtime debugging feature. For instance, if you wanted to listen to all messages then you can just pass in a wildcard:
134+
135+
```javascript
136+
Hub.listen(/.*/, (data) => {
137+
console.log('Listening for all messages: ', data.payload.data)
138+
})
139+
```
140+
141+
When using a "Capturing Group" (e.g. parenthesis grouping regular expressions) an array will be populated called `patternInfo` and returned as part of your callback:
142+
143+
```javascript
144+
Hub.listen(/user(.*)/, (data) => {
145+
console.log('A USER event has been found matching the pattern: ', data.payload.message);
146+
console.log('patternInfo:', data.patternInfo);
147+
})
148+
```
149+
150+
For example, this can be useful if you want to extract the text before and/or after a specific phrase:
151+
152+
```javascript
153+
Hub.listen(/user ([^ ]+) ([^ ]+) (.*)/, (data) => {
154+
console.log('A USER event has been found matching the pattern: ', data.payload.message);
155+
console.log('patternInfo:', data.patternInfo);
156+
})
157+
```
158+
159+
### State Management
160+
161+
Hub can be used as part of a state management system such as [Redux](https://redux.js.org/) or [MobX](https://github.com/mobxjs/mobx) by updating the store when an event is seen by one or more listeners. You could also construct your own local store. For example, suppose you have the following in a React application's top level component:
162+
163+
```javascript
164+
const store = (() => {
165+
const listeners = [];
166+
167+
const theStore = {
168+
subscribe(listener) {
169+
listeners.push(listener);
170+
}
171+
};
172+
173+
return new Proxy(theStore, {
174+
set(_obj, _prop, _value) {
175+
listeners.forEach(l => l());
176+
return Reflect.set(...arguments);
177+
}
178+
});
179+
})();
180+
181+
Hub.listen(/.*/, (data) => {
182+
console.log('Listening for all messages: ', data.payload.data)
183+
if (data.payload.message){
184+
store['message-' + Math.floor(Math.random() * 100)] = data.payload.message
185+
}
186+
})
187+
188+
class App extends Component {
189+
190+
addItem = () => {
191+
Hub.dispatch('MyGroup', {
192+
data : { a: 1},
193+
event: 'clicked',
194+
message: 'A user clicked a button'
195+
})
196+
console.log(store);
197+
}
198+
199+
render() {
200+
console.log('store: ', store)
201+
return (
202+
<div className="App">
203+
<button onClick={this.addItem}>Add item</button>
204+
<DogAlerts store={store}/>
205+
<DogStatus store={store}/>
206+
</div>
207+
);
208+
}
209+
}
210+
```
211+
212+
This naive sample (which is for example purposes and not production ready) creates a `store` that is updated when events are received by `Hub.listen()` if there is a message present in the payload. We then create these messages with `Hub.dispatch()` upon a button click and pass the store down to two components called `<DogAlerts />` and `<DogStatus />`. The first is a very simple stateless component that renders the current store value from props:
213+
214+
```javascript
215+
const DogAlerts = (props) => {
216+
return <pre>{JSON.stringify(props, null, 2)}</pre>
217+
}
218+
```
219+
220+
While this is nice functionality, when the button is clicked the component will not be updated. In order to do that you can use a class component (or React Hooks) and call `store.subscribe` as part of the `componentDidMount()` lifecycle method:
221+
222+
```javascript
223+
class DogStatus extends Component {
224+
componentDidMount(){
225+
this.props.store.subscribe(()=>{
226+
this.forceUpdate();
227+
})
228+
}
229+
230+
render(){
231+
return(<div>
232+
<pre>Dog Status</pre>
233+
<pre>{JSON.stringify(this.props, null, 2)}</pre>
234+
</div>)
235+
}
236+
}
237+
```
238+
239+
Now when the store is updated the `<DogStatus />` component re-renders on the screen.
240+
88241
### API Reference
89242

90243
For the complete API documentation for Hub module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/hubclass.html)

0 commit comments

Comments
 (0)