1 | # React Side Effect
|
2 | Create components whose prop changes map to a global side effect.
|
3 |
|
4 | ## Installation
|
5 |
|
6 | ```
|
7 | npm install --save react-side-effect
|
8 | ```
|
9 |
|
10 | ## Use Cases
|
11 |
|
12 | * Setting `document.style.overflow` or background color depending on current screen;
|
13 | * Firing Flux actions using declarative API depending on current screen;
|
14 | * Some crazy stuff I haven't thought about.
|
15 |
|
16 | ## How's That Different from `componentDidUpdate`?
|
17 |
|
18 | It gathers current props across *the whole tree* before passing them to side effect. For example, this allows you to create `<BodyStyle style>` component like this:
|
19 |
|
20 | ```js
|
21 | // RootComponent.js
|
22 | return (
|
23 | <BodyStyle style={{ backgroundColor: 'red' }}>
|
24 | {this.state.something ? <SomeComponent /> : <OtherComponent />}
|
25 | </BodyStyle>
|
26 | );
|
27 |
|
28 | // SomeComponent.js
|
29 | return (
|
30 | <BodyStyle style={{ backgroundColor: this.state.color }}>
|
31 | <div>Choose color: <input valueLink={this.linkState('color')} /></div>
|
32 | </BodyStyle>
|
33 | );
|
34 | ```
|
35 |
|
36 | and let the effect handler merge `style` from different level of nesting with innermost winning:
|
37 |
|
38 | ```js
|
39 | var BodyStyle = createSideEffect(function handleChange(propsList) {
|
40 | var style = {};
|
41 | propsList.forEach(function (props) {
|
42 | Object.assign(style, props.style);
|
43 | });
|
44 |
|
45 | for (var key in style) {
|
46 | document.style[key] = style[key];
|
47 | }
|
48 | });
|
49 | ```
|
50 |
|
51 |
|
52 | ## API
|
53 |
|
54 | #### `createSideEffect: (onChange: Array<Props> -> (), mixin: Object?) -> ReactComponent`
|
55 |
|
56 | Returns a component that, when mounting, unmounting or receiving new props, calls `onChange` with `props` of **each mounted instance**.
|
57 | It's up to you to `reduce` them, use innermost values, or whatever you fancy.
|
58 |
|
59 | Component will have a static `dispose()` method to clear the stack of mounted instances.
|
60 | When rendering on server, you must call it after each request.
|
61 |
|
62 | You can use optional second `mixin` parameter to specify `propTypes`, `displayName` or `statics`. It will be mixed into the generated component.
|
63 |
|
64 | ## Usage
|
65 |
|
66 | Here's how to implement [React Document Title](https://github.com/gaearon/react-document-title) (both client and server side) using React Side Effect:
|
67 |
|
68 | ```js
|
69 | 'use strict';
|
70 |
|
71 | var React = require('react'),
|
72 | createSideEffect = require('react-side-effect');
|
73 |
|
74 | /**
|
75 | * Extract title from a list of each mounted component's props.
|
76 | * We're interested in the innermost title, but for other use cases we might want to call `propList.reduce`.
|
77 | */
|
78 | function extractTitle(propsList) {
|
79 | var innermostProps = propsList[propsList.length - 1];
|
80 | if (innermostProps) {
|
81 | return innermostProps.title;
|
82 | }
|
83 | }
|
84 |
|
85 | var _serverTitle = null;
|
86 |
|
87 | /**
|
88 | * Generate a component that reacts to mounting, onmounting and prop changes by updating document title.
|
89 | */
|
90 | var DocumentTitle = createSideEffect(function handleChange(propsList) {
|
91 | var title = extractTitle(propsList);
|
92 |
|
93 | if (typeof document !== 'undefined') {
|
94 | document.title = title || '';
|
95 | } else {
|
96 | _serverTitle = title || null;
|
97 | }
|
98 | }, {
|
99 | displayName: 'DocumentTitle',
|
100 |
|
101 | propTypes: {
|
102 | title: React.PropTypes.string.isRequired
|
103 | },
|
104 |
|
105 | statics: {
|
106 | /**
|
107 | * Peek at current title (for tests).
|
108 | */
|
109 | peek: function () {
|
110 | return _serverTitle;
|
111 | },
|
112 |
|
113 | /**
|
114 | * Call this on server after each request to get current title.
|
115 | */
|
116 | rewind: function () {
|
117 | var title = _serverTitle;
|
118 | this.dispose();
|
119 | return title;
|
120 | }
|
121 | }
|
122 | });
|
123 |
|
124 | module.exports = DocumentTitle;
|
125 | ```
|