Skip to content

Commit 6652f4d

Browse files
committed
Doc fixes
1 parent e0a37d2 commit 6652f4d

File tree

2 files changed

+47
-28
lines changed

2 files changed

+47
-28
lines changed

CHANGELOG.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
## 0.2
1+
# 0.2
22

3-
### **BREAKING CHANGE:** require() targeting package root no longer supported.
3+
This is a major release that brings some changes that my team is excited about.
44

5-
In 0.1.x, under CommonJS, you could `require()` specific submodules with e.g. `require('redux-components/createClass')`. This cherry-picking is no longer supported.
5+
We want to know what you think about redux-components. As always, feel free to file an issue if you find a bug or want a feature.
66

7-
Redux-components is moving to the ES Modules + Babel model, so individual submodules are exported from the `index` module.
7+
We'd also love to see more people using the library. Many people in the Redux community are now catching on to this concept (e.g. [redux-interactions](https://github.com/convoyinc/redux-interactions), [redux-modules](https://github.com/procore/redux-modules)) which leads me to believe there's something important at the bottom here. If you like redux-components, tell your friends! If you're using redux-components (or another library like redux-components) tell us what works and doesn't work for you.
88

9-
The correct way to pick out modules going forward is `{ createClass } = require('redux-components')`.
9+
### **BREAKING CHANGE:** CommonJS require() cherry-picking from package root is no longer supported.
10+
11+
In 0.1.x, under CommonJS, you could `require()` specific submodules with e.g. `require('redux-components/createClass')`. This cherry-picking is no longer supported. Redux-components is moving to the ES Modules + Babel model, so individual submodules are exported from the `index` module.
12+
13+
The correct way to pick out submodules under CommonJS going forward is `{ createClass } = require('redux-components')`.
14+
15+
Unfortunately, the docs were written in the cherry-picking style, so users of the library are likely doing so as well. The docs have been updated to reflect the Babel+CommonJS style.
1016

1117
### ES2015 Module build
1218

13-
0.2.x now exports appropriate `jsnext:main` and `module` entries from `package.json` allowing use as an ES2015 module. CommonJS is still supported subject to the caveats above.
19+
0.2.x now exports appropriate `jsnext:main` and `module` entries from `package.json` allowing for `import` as an ES2015 module:
20+
21+
```coffeescript
22+
import { createClass } from 'redux-components'
23+
```
24+
25+
CommonJS is still supported subject to the caveats above.
1426

1527
### Git Submodule support
1628

docs/Advanced/ObservableSelector.md

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
1-
# Dynamic Reducers
1+
# Observable Selectors
22

3-
It is occasionally necessary to modify reducer behavior within a running app, as evidenced by Redux's own `store.replaceReducer` function. In 0.2, Redux Components is introducing its own version of this behavior, which allows you to build predictable dynamic reducer behaviors into your components.
3+
Redux 3.6.x adds the ability for Stores to function as [ES7 Observables](https://github.com/tc39/proposal-observable). Building on this capability, Redux Components 0.2.x now allows `selectors` that are specified on your components to be used as [Observables](https://github.com/tc39/proposal-observable) as well.
44

5-
> **NB:** Dynamic reducers are an advanced and dangerous feature. It is very easy to break the Redux contract by abusing a dynamic reducer. Dynamic reducers are rarely necessary and you should only use them if you are completely certain that you need them. As correct dynamic reducers are tricky to write, you should also prefer pre-written and tested libraries that use dynamic reducers to writing your own whenever practical.
5+
## Functionality
66

7-
## When do I need a dynamic reducer?
7+
Observable selectors are provided by adding the `ObservableSelectorMixin` to your component class:
8+
```coffeescript
9+
{ ObservableSelectorMixin, createClass } = require 'redux-components'
810

9-
If your reducer needs to behave in a fundamentally different way based on the state of your application, and you **cannot** implement this behavior as a single pure function of state, then you need a dynamic reducer.
11+
MyClass = createClass {
12+
mixins: [ ObservableSelectorMixin ]
13+
...
14+
selectors: {
15+
mySelector: (state) -> state.myValue
16+
}
17+
...
18+
}
19+
```
1020

11-
An example is the `Map` class implemented in redux-component-map, which allows Redux Components to be dynamically mounted and unmounted from a node of the state tree of a live application.
21+
Behind the scenes, this mixin ensures that when an instance of your component is mounted, all of the `selectors` on the instance will be augmented to conform to the [ES7 Observable](https://github.com/tc39/proposal-observable) pattern.
1222

13-
In order to implement this behavior, it is necessary to define a new reducer function each time the tree of components changes, because the reducer function is a composition of other functions and the composition depends on the shape of the tree. This is the typical use case for which dynamic reducers were developed.
23+
Here are the details:
1424

15-
Dynamic reducers are dangerous in general. Most of the time someone needs a dynamic reducer, it is basically to implement `Map` or something like it. If that's you, consider redux-component-map before writing your own.
25+
- To watch an observable selector for changes, you call the `subscribe(Observer)` method on the selector, passing an object that conforms to the ES7 `Observer` interface. The `subscribe` method returns an ES7 `Subscription` object that can be used to unsubscribe later.
26+
```coffeescript
27+
subscription = myClassInstance.mySelector.subscribe({
28+
next: (value) -> console.log "mySelector just changed value:", value
29+
})
30+
...
31+
subscription.unsubscribe()
32+
```
1633

17-
## How do I create a component with a dynamic reducer?
34+
- An observable selector will call `observer.next(value)` on each observer immediately with the value of the selector at initialization time, and then subsequently whenever the value returned by the selector would change. It always passes the current value of the selector.
1835

19-
If you [specify a component](/docs/API/ComponentSpecification.md) with a `getReducer` function taking zero arguments, you are creating a `ReduxComponent` with a *static reducer*. `getReducer` will be called once, when the component is mounted, and never again throughout the lifecycle.
36+
- An observable selector is what is sometimes called a "hot observable" -- it never calls `observer.complete()`. It also never calls `observer.error()`, even if your selector throws an error.
2037

21-
If, however, you specify a `getReducer` function taking one or more arguments (as measured by `Function.length`), you are specifying a *dynamic reducer*. If you do this, then the internal behavior of the component changes:
38+
- Observable selectors assume conformance with the Redux contract, and therefore use shallow (`===`) equality to detect changes. Your Redux stores are expected to return unequal objects when changes are made.
2239

23-
- `getReducer` will be passed the current state of the component at call-time as its first argument.
24-
- The final component will have a thunk reducer that will call whatever you most recently returned from `getReducer`, allowing you to update your reducer dynamically.
25-
- Your component instance will have an `updateReducer()` method that will cause `getReducer` to be invoked. `updateReducer` is impure. **DO NOT** call `updateReducer` from inside of a reducer.
26-
27-
## What are the dangers of dynamic reducers?
28-
29-
- You should think of "the state of your dynamic reducer" as part of your app's state. When Redux rehydrates your store from a serialized state, you must ensure your dynamic reducer can be recovered during the hydration. Like reducers themselves, the `getReducer()` that produces your dynamic reducers must be a pure function of state. If your `getReducer()` impurely depends on app state that is outside of Redux, you are virtually guaranteed to break the Redux contract and lose coherence under state rehydration.
30-
31-
- You are responsible for keeping your reducer up to date. In particular, you must arrange for `updateReducer()` to be called whenever a state change would impact your dynamic reducer. If you do not synchronize this correctly, you will again almost certainly break Redux's contract.
32-
33-
- `updateReducer()` is of course an impure operation and cannot be called within a reducer. Thus you must ensure that your synchronization system runs in an impure context, e.g. on `store.subscribe()`. Observable selectors can help you implement this pattern correctly.
40+
- Your component must be mounted to a stored before you may subscribe to observable selectors.

0 commit comments

Comments
 (0)