reaction-hooks 0.0.23 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +296 -0
  2. package/example.js +1366 -165
  3. package/index.html +9 -24
  4. package/lib/example/hooksApp/useContext/button/link/close.js +3 -4
  5. package/lib/example/hooksApp/useContext/button/link/open.js +3 -4
  6. package/lib/example/hooksApp/useContext/component.js +2 -3
  7. package/lib/example/hooksApp/useContext/section/gotIt.js +1 -3
  8. package/lib/example/hooksApp/useEffects/article/home.js +136 -0
  9. package/lib/example/hooksApp/useEffects/article/settings.js +136 -0
  10. package/lib/example/hooksApp/useEffects/article.js +146 -0
  11. package/lib/example/hooksApp/useEffects/button/home.js +113 -0
  12. package/lib/example/hooksApp/useEffects/button/settings.js +113 -0
  13. package/lib/example/hooksApp/useEffects/button.js +144 -0
  14. package/lib/example/hooksApp/useEffects/component.js +123 -0
  15. package/lib/example/hooksApp/useEffects/constants.js +26 -0
  16. package/lib/example/hooksApp/useEffects/navigation/primary.js +25 -0
  17. package/lib/example/hooksApp/useEffects/section/articles.js +25 -0
  18. package/lib/example/hooksApp/useEffects/section/main.js +140 -0
  19. package/lib/example/hooksApp/useState/component.js +3 -4
  20. package/lib/example/hooksApp/view.js +5 -5
  21. package/package.json +1 -1
  22. package/src/example/hooksApp/useContext/button/link/close.js +0 -2
  23. package/src/example/hooksApp/useContext/button/link/open.js +0 -2
  24. package/src/example/hooksApp/useContext/component.js +0 -2
  25. package/src/example/hooksApp/useContext/section/gotIt.js +0 -4
  26. package/src/example/hooksApp/useEffects/article/home.js +19 -0
  27. package/src/example/hooksApp/useEffects/article/settings.js +19 -0
  28. package/src/example/hooksApp/useEffects/article.js +24 -0
  29. package/src/example/hooksApp/useEffects/button/home.js +9 -0
  30. package/src/example/hooksApp/useEffects/button/settings.js +9 -0
  31. package/src/example/hooksApp/useEffects/button.js +27 -0
  32. package/src/example/hooksApp/useEffects/component.js +15 -0
  33. package/src/example/hooksApp/useEffects/constants.js +5 -0
  34. package/src/example/hooksApp/useEffects/navigation/primary.js +15 -0
  35. package/src/example/hooksApp/useEffects/section/articles.js +15 -0
  36. package/src/example/hooksApp/useEffects/section/main.js +33 -0
  37. package/src/example/hooksApp/useState/component.js +0 -2
  38. package/src/example/hooksApp/view.js +16 -5
package/README.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Hooks for [Reaction](https://github.com/djalbat/reaction).
4
4
 
5
+ These are inspired by React's hooks but do not follow them slavishly. Three are three available:
6
+
7
+ * `useState()` A state management hook that does indeed closely follow React's hook of the same name. It also supports created classes and components, however.
8
+ * `useContext()` A hook that leverages contexts in order to enable components directly related to one another in the DOM to communicate. Typically component methods are passed via the context so that one component can call the methods of another.
9
+ * `useEffects()` A hook that use Reaction's in-built updates functionality in order to enable components to communicate no matter their relative positions in the DOM. It is also possible to communicate with other parts of an application in this manner.
10
+
11
+ The `useEffects()` hook in fact bears very little resemblance to React's hook of the same name and is more akin to [Inference](https://github.com/djalbat/inference) but without the rules.
12
+
5
13
  ## Installation
6
14
 
7
15
  With [npm](https://www.npmjs.com/):
@@ -18,6 +26,294 @@ You can also clone the repository with [Git](https://git-scm.com/)...
18
26
 
19
27
  You can also run a development server, see the section on building later on.
20
28
 
29
+ ## Usage
30
+
31
+ The three hooks are imported thus:
32
+
33
+ ```
34
+ import { useState, useContext, useEffects } from "reaction-hooks";
35
+ ```
36
+
37
+ The only other function is `emitEffect()`, which can be obtained by destructuring the `useEffects()` hook:
38
+
39
+ ```
40
+ const { emitEffect } = useEffects;
41
+ ```
42
+
43
+ Detailed usages are outlined in the recommanded patterns section that follows the example section.
44
+
45
+ ## Example
46
+
47
+ Launch the `example.html` file. There is a single hooks example which encapsulates all of the hooks.
48
+
49
+ ## Recommended patterns
50
+
51
+ Each of the hooks are covered with the listings closely matching the example code.
52
+
53
+ ### `useState()`
54
+
55
+ There are three ways to use the `useState()` hook.
56
+
57
+ The first will be familiar to React users as it enables functional elements to maintain state:
58
+
59
+ ```
60
+ const initialCount = 0;
61
+
62
+ const FunctionUseStateParagraph = (props, context, update, element) => {
63
+ const [ count, setCount ] = useState(element, initialCount);
64
+
65
+ return (
66
+
67
+ <p onClick={(event) => setCount(count + 1)}>
68
+ Function state: {`${count}`}
69
+ </p>
70
+
71
+ );
72
+ };
73
+ ```
74
+ Note that the last of the four function arguments is a reference to the function's corresponding element. The details are unimportant, however this reference must be passed as the first argument of the `useState()` call. Otherwise the usage is entirely analogous to the corresponding React hook.
75
+
76
+ Because the `useState()` hook is passed a reference to the corresponding element under the hood, so to speak, it should come as no surprise that can also support created classes and components, which also both have corresponding underlying elements.
77
+
78
+ The second way is with created classes. The name is a misnomer as they are not classes and they are, with the advent of components, rarely if ever used anymore. Nonetheless they are supported:
79
+
80
+ ```
81
+ const { createClass } = React;
82
+
83
+ let initialCount = 0;
84
+
85
+ const CreateClassUseStateParagraph = createClass({
86
+ render: function(update) {
87
+ const [ count, setCount ] = useState(this, initialCount);
88
+
89
+ return (
90
+
91
+ <p onClick={(event) => setCount(count + 1)}>
92
+ createClass state: {`${count}`}
93
+ </p>
94
+
95
+ );
96
+ }
97
+ });
98
+ ```
99
+
100
+ Essentially the only difference between this usage and the functional usage is that the `useState()` call takes place within the `render()` function.
101
+
102
+ Lastly, genuine components, which are treated identically:
103
+
104
+ ```
105
+ const { Component } = React;
106
+
107
+ let initialCount = 0;
108
+
109
+ export default class ComponentUseStateParagraph extends Component {
110
+ render(update, element) {
111
+ const [ count, setCount ] = useState(element, initialCount);
112
+
113
+ return (
114
+
115
+ <p onClick={(event) => setCount(count + 1)}>
116
+ Component state: {`${count}`}
117
+ </p>
118
+
119
+ );
120
+ }
121
+ }
122
+ ```
123
+
124
+ Of course created classes and components support state without the need for a hook. At the risk of repetition, support for the `useState()` hook cost virtually nothing and is included more for the sake of completeness than for anything else.
125
+
126
+ ### `useContext()`
127
+
128
+ This hook can be used comprehensively to share information, most likely methods, between components and the like that are directly related in the DOM. What this means is that in order for one component to share information with another it must be a descendant of the other or vice-versa. Since this hook leverages contexts, perhaps this is not so surprising. In fact another lifecycle method, namely the `childContextSet()`lifecycle method, was added to Reaction in order to support it.
129
+
130
+ At the topmost level of the example component both this lifecycle method and the more commonly known `setChildContext()` lifecycle method are utilised:
131
+
132
+ ```
133
+ getChildContext(context) {
134
+ const openLinkButtonClickHandler = this.openLinkButtonClickHandler,
135
+ closeLinkButtonClickHandler = this.closeLinkButtonClickHandler;
136
+
137
+ return({
138
+ openLinkButtonClickHandler,
139
+ closeLinkButtonClickHandler
140
+ });
141
+ }
142
+
143
+ childContextSet(childContext) {
144
+ Object.assign(this, childContext);
145
+ }
146
+ ```
147
+
148
+ This usage is noteworthy because it does not make use of the `useContext()` hook at all. A more primitive approach is used here to emphasise the fact that the object created in the `createChildContext()` lifecycle method is not only passed down to the component's children but is also precisely the one that is passed to the component's own `childContextSet()` lifecycle method.
149
+
150
+ A more conventional and indeed the recommended approach is to make use of the `useContetx()` hook in both lifecycle methods in each of its respective guises:
151
+
152
+ ```
153
+ getChildContext(context) {
154
+ const openLinkButtonClickHandler = this.openLinkButtonClickHandler,
155
+ closeLinkButtonClickHandler = this.closeLinkButtonClickHandler;
156
+
157
+ useContext(this, context, {
158
+ openLinkButtonClickHandler,
159
+ closeLinkButtonClickHandler
160
+ });
161
+
162
+ return context;
163
+ }
164
+
165
+ childContextSet(childContext) {
166
+ useContext(this. childContext);
167
+ }
168
+ ```
169
+ In the `getChildContext()` lifecycle method, two of the topmost component's own methods are assigned to the context, a reference to which is then passed to the component's children. Conversely, in the `childContextSet()` lifecycle method, whatever methods are on the child context at this point, which will have been assigned by the children, are assigned to the topmost component.
170
+
171
+ Beginning to look down at the component's children, we see that the `GotItHeader` component adds two of its own methods to the context:
172
+
173
+ ```
174
+ export default class GotItHeader extends Component {
175
+ getChildContext(context) {
176
+ const showGotItHeader = this.show.bind(this), ///
177
+ hideGotItHeader = this.hide.bind(this); ///
178
+
179
+ useContext(this, context, {
180
+ showGotItHeader,
181
+ hideGotItHeader
182
+ });
183
+
184
+ return context;
185
+ }
186
+
187
+ ...
188
+ }
189
+ ```
190
+ These will eventually be picked up by the topmost component in its aforementioned `childContextSet()` lifecycle method. Indeed we can see which methods of the component's children it ends up assigning to itself by looking at some of its other methods:
191
+
192
+ ```
193
+ close() {
194
+ this.hideGotItDiv();
195
+ this.showGotItHeader();
196
+ }
197
+
198
+ open() {
199
+ this.showGotItDiv();
200
+ this.hideGotItHeader();
201
+ }
202
+ ```
203
+
204
+ By way of contrast, the descendant `OpenLinkButton` component grabs one of the topmost component's methods from the context and assigns it to itself. The method is then made use of in its `render()` method:
205
+
206
+ ```
207
+ export default class OpenLinkButton extends Component {
208
+ getChildContext(context) {
209
+ useContext(this, context, [
210
+ "openLinkButtonClickHandler"
211
+ ]);
212
+ }
213
+
214
+ render(update) {
215
+ const { children } = this.props,
216
+ clickHandler = this.openLinkButtonClickHandler; ///
217
+
218
+ return (
219
+
220
+ <button className="open link" onClick={clickHandler}>
221
+ {children}
222
+ </button>
223
+
224
+ );
225
+ }
226
+ }
227
+ ```
228
+ Essentially then the `useContext()` hook allows methods and the like to piggy back on the context, so to speak, so that components can pick them up and make use of them. Thus related components can call methods on each other without the need for more complex or indirect mechanisms such as message passing.
229
+
230
+ ### `useEffects()`
231
+
232
+ This hook was added to solve the problem of communication between components that do not share the same context, or to allow other parts of the application to communicate with components or vice-versa. Essentially an effect is a named object, it can be a plain old JavaScript object, a function, a class instance or whatever, that is emitted by one part of the application and subsequently used by another.
233
+
234
+ In the example, primary navigation buttons emit effects that are then picked up by articles, which hide or show themselves depending on whether or not their names match the effects. Specifically, there is a base `Button` component that emits an `articleName` effect...
235
+
236
+ ```
237
+ export default class Button extends Component {
238
+ clickHandler = (event) => {
239
+ const { articleName } = this.constructor;
240
+
241
+ emitEffect("articleName", articleName);
242
+ }
243
+
244
+ render(update) {
245
+ const { text } = this.constructor;
246
+
247
+ return (
248
+
249
+ <button onClick={this.clickHandler}>
250
+ {text}
251
+ </button>
252
+
253
+ );
254
+ }
255
+ }
256
+ ```
257
+
258
+ ...the sub-components of which are configured to correspond to a particular article:
259
+
260
+ ```
261
+ import Button from "../button";
262
+
263
+ export default class HomeButton extends Button {
264
+ static text = "Home";
265
+
266
+ static articleName = "home";
267
+ }
268
+ ```
269
+
270
+ The corresponding `Article` component uses this effect...
271
+
272
+ ```
273
+ export default class Article extends Component {
274
+ updateHandler = (update) => {
275
+ const { name } = this.constructor,
276
+ { articleName } = update;
277
+
278
+ (name === articleName) ?
279
+ this.show() :
280
+ this.hide();
281
+ }
282
+
283
+ componentDidMount() {
284
+ this.discardEffects = useEffects(this.updateHandler, "articleName");
285
+ }
286
+
287
+ componentWillUnmount() {
288
+ this.discardEffects();
289
+ }
290
+ }
291
+ ```
292
+ ...with again its sub-components being configured with specific names:
293
+
294
+ ```
295
+ import Article from "../article";
296
+
297
+ export default class HomeArticle extends Article {
298
+ static name = "home";
299
+
300
+ render(update) {
301
+ ...
302
+ }
303
+ }
304
+ ```
305
+ It is usual to configure a component to call an `updateHandler()` handler method in response to an effect being used, the reason being that whereas in this case the body of the handler method is benign in the sense that the component is not redrawn, often it is the case that the component does need to be redrawn, in which case Reaction provides a standard mechanism to do so. Precisely:
306
+
307
+ ```
308
+ updatehandler(update) {
309
+ this.forceUpdate();
310
+ }
311
+ ```
312
+
313
+ Underneath the hood the `forceUpdate()` method will unmount the component's children and then mount the new children returned by invoking the component's `render()` method, which is passed the requisite update. In fact the update is just a plain old JavaScript object the single property of which is named after the effect and its value is the effect itself.
314
+
315
+ Finally, note that a component or indeed any part of the application can be configured to listen to several effects as additional arguments to the `useEffects()` hook, hence the use of the plural, in which cases the encapculation of each effect in a plain old JavaScript object becomes invaluable for ascertaining which effect has been emitted.
316
+
21
317
  ## Building
22
318
 
23
319
  Automation is thanks to [npm scripts](https://docs.npmjs.com/misc/scripts), have a look at the `package.json` file. The pertinent commands are: