Skip to content

Commit e0a37d2

Browse files
committed
- Doc updates
- Removed root-level require() capabilities
1 parent eb98426 commit e0a37d2

18 files changed

+232
-139
lines changed

.npmignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ build/
99
book.json
1010
coffeelint.json
1111
.eslintrc
12-
.babelrc

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
## 0.2
2+
3+
### **BREAKING CHANGE:** require() targeting package root no longer supported.
4+
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.
6+
7+
Redux-components is moving to the ES Modules + Babel model, so individual submodules are exported from the `index` module.
8+
9+
The correct way to pick out modules going forward is `{ createClass } = require('redux-components')`.
10+
11+
### ES2015 Module build
12+
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.
14+
15+
### Git Submodule support
16+
17+
Some people on my team were upset about how I broke Git submodule support in 0.1, so I fixed it for 0.2. You can now check out redux-components directly from Git as a submodule beneath some `node_modules` folder in your project. After an initial `npm install` to set up the git hooks, everything should work automatically, including a `post-merge` hook to rebuild the `lib` and `es` artifacts whenever you merge from upstream.
18+
19+
### Action Dispatchers
20+
21+
A new component specification entry, `spec.actionDispatchers`, has been added:
22+
```coffeescript
23+
spec.actionDispatchers = { key: (args...) => action, ... }
24+
```
25+
26+
Action dispatchers are like action creators, except that they are automatically wrapped with `dispatch()` for the appropriate Redux Store when the component is mounted. Calling an action dispatcher will save you the extra step of calling `dispatch` yourself.
27+
28+
>**NB:** Your action dispatchers must return actions, just as action creators do.
29+
30+
### Reducer Indirection and Dynamic Reducers
31+
32+
- The `reducer` property of each `ReduxComponent` instance is now a lightweight thunk reducer that indirectly calls the last return value from `getReducer`.
33+
34+
- `spec.getReducer` has a new signature:
35+
```coffeescript
36+
spec.getReducer = (state?) => (state, action) => nextState
37+
```
38+
39+
- If you register a class with a `getReducer` function taking zero arguments, the internal behavior of the `ReduxComponent` is the same as it was in 0.1.x.
40+
41+
- If you create a class with a `getReducer` function taking one or more arguments, the internal behavior of the `ReduxComponent` changes:
42+
- `getReducer` will be passed the current state of the component as its first argument.
43+
- The thunk reducer will call whatever you return from `getReducer`, allowing you to update your reducer dynamically.
44+
- Your `ReduxComponent` 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.
45+
46+
> **NB:** Dynamic reducers are an advanced and dangerous feature. You should only use them if you are completely certain that you need them. Don't forget the Redux contract!
47+
- Reducers should be pure functions of state and action. Dynamic reducer behavior should only be used when you are sure you can honor this contract.
48+
- In particular, you should think of "the state of your dynamic reducer" as being a part of your app's overall state.
49+
- That means the behavior of a dynamic reducer should be a pure function of some branch of your state tree.
50+
- If you make dynamic reducer behavior depend on stateful information that isn't stored in Redux, you are virtually guaranteed to break core Redux features like time travel and rehydration.
51+
52+
### Observable Selectors
53+
54+
A new mixin, `ObservableSelectorMixin`, has been provided. When mixed into a component, all `selectors` declared on the component will be instantiated as [ES7 Observables](https://github.com/tc39/proposal-observable) when the component is mounted.
55+
56+
If `componentInstance` is an instance of a component that uses `ObservableSelectorMixin`, and `selector` is one of its selectors, then `componentInstance.selector.subscribe(observer)` will invoke `observer.next(value)` whenever the value returned by the selector changes.
57+
58+
> The observable implementation assumes your store's state obeys the Redux contract, so `===` equality is used to compare selector values.

ReduxComponent.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

SubtreeMixin.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

createClass.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

createComponent.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

docs/API/ComponentSpecification.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Component Specification
2+
3+
A component specification is a plain JavaScript object that declaratively specifies the behavior of a component class. This object is passed to `createClass`, which in turn builds a `ReduxComponent` matching the spec.
4+
5+
Generally speaking, any function assigned to a key on the specification object will be copied into the prototype of the class and thus become available as instance methods. The exception to this rule is for certain specially-named keys which are used by redux-components to implement core component behavior. The special keys are as follows:
6+
7+
## Meta properties
8+
9+
### spec.displayName
10+
```coffeescript
11+
spec.displayName = string
12+
```
13+
If specified, gives a name to this component that can be used by debugging and development tools.
14+
15+
> Just as with React, relying on displayName to do type checking in production is an antipattern. Use the instanceof operator.
16+
17+
### spec.statics
18+
```coffeescript
19+
spec.statics = { ... }
20+
```
21+
All keys and values from ```spec.statics``` are merged onto the constructor for the component class via ```Object.assign```. This makes them available as "static methods" on the class constructor.
22+
23+
### spec.mixins
24+
```coffeescript
25+
spec.mixins = [ {spec}, {spec}, ... ]
26+
```
27+
An array of additional specs that will be mixed into this spec before creating the class. Mixins are processed in the order they appear in the array. In general, the keys on the mixin specs will be assigned to the base spec as with ```Object.assign```, with auto-binding for functions. There is special behavior for certain keys. See [Mixins](Mixins.md) for more.
28+
29+
## Lifecycle methods
30+
31+
### spec.componentWillMount
32+
```coffeescript
33+
spec.componentWillMount = =>
34+
```
35+
### spec.componentDidMount
36+
```coffeescript
37+
spec.componentDidMount = =>
38+
```
39+
### spec.componentWillUnmount
40+
```coffeescript
41+
spec.componentWillUnmount = =>
42+
```
43+
Specifies the lifecycle methods for instances of the class. See [Components](Components.md) for details on when the methods are called.
44+
45+
## Redux-related properties
46+
47+
### spec.getReducer
48+
```coffeescript
49+
spec.getReducer = => (state, action) => nextState
50+
```
51+
getReducer returns a reducer function that will be used for the state subtree managed by this component.
52+
53+
Reducers made by getReducer are automatically bound to their component instances so that they can have access to scoped properties, particularly scoped action verbs.
54+
55+
> **NB:**
56+
> - After all mixins are evaluated, a final spec must have exactly one ```.getReducer```. We prefer to be unopinionated, so by default, there is no specified behavior for composing multiple reducers. (But see ```SubtreeMixin```.)
57+
58+
> - Do not use magic binding as an excuse to introduce impure behavior into your reducer! If you want the all the benefits of Redux, keep your reducers as pure functions of props and state. Don't make your reducer rely on non-constant properties of the redux-component, and don't be tempted to store any state on the redux-component itself. (You can sometimes use ```getReducer``` to mimic "impure" behaviors without making your reducer itself impure.)
59+
60+
> - By default, redux-components will call `getReducer()` only once when your component is mounted to a state tree. If you expect your reducer to change during the Redux store's lifecycle, you must arrange for `getReducer()` to be called at appropriate times, and for `Store.replaceReducer()` to be called on your root store. If you use redux-components and `SubtreeMixin` throughout your state tree, we provide facilities for automating this.
61+
62+
> - You may optionally declare a `getReducer` method that accepts a `stateNow` argument:
63+
```coffeescript
64+
spec.getReducer = (stateNow) => (state, action) => nextState
65+
```
66+
> If you do, your reducer is considered to be a [dynamic reducer](/docs/Advanced/DynamicReducer.md). The JavaScript `Function.length` property is used to make this determination. Dynamic reducers are an advanced feature and should not generally be used. Please read [the dynamic reducer documentation](/docs/Advanced/DynamicReducer.md) carefully before use.
67+
68+
### spec.verbs
69+
```coffeescript
70+
spec.verbs = [ string, string, ... ]
71+
```
72+
Specifies a list of action names that will be scoped to each instance of the component using the instance's path in the state tree. Verbs are scoped according to the following pattern: ```#{path}:#{verb}``` and are available on the instance object at a key corresponding to the verb root. For example, a component instance mounted at ```foo.bar``` in the state tree having a verb ```"BAZ"``` would have the scoped verb ```"foo.bar:BAZ"``` available as ```this.BAZ```.
73+
74+
> If you don't want a verb to be scoped, don't put it in the verbs array. Instead set it as a plain string property on the specification. It will then bypass the auto-scoping behavior.
75+
76+
### spec.actionCreators
77+
```coffeescript
78+
spec.actionCreators = { key: (args...) -> action, ... }
79+
```
80+
Specify the action creators associated with this component class. Each property on the ```actionCreators``` object will be bound to each component instance and made available on the instance as a method.
81+
> **NB:** The redux-components core makes no assumptions about which middleware is present on a store. If you have e.g. `redux-thunk` middleware installed, you may of course return thunks from your actions and actionCreators.
82+
83+
### spec.actionDispatchers
84+
```coffeescript
85+
spec.actionDispatchers = { key: (args...) -> action, ... }
86+
```
87+
Specify action dispatchers associated with this component class. Action dispatchers are like action creators, except the value returned is automatically `dispatch()`ed to the Store where this component is mounted. The wrapped action function will return the same thing as `dispatch()`, allowing it to be used with thunks and other related middleware.
88+
89+
### spec.selectors
90+
```coffeescript
91+
spec.selectors = { key: (state, ...) -> any, ... }
92+
```
93+
Specify the selectors associated with this component class. Each property on the `selectors` object will be bound to each component instance and wrapped in a function that scopes the selector to the instance's state. The net effect will be that the state argument received by the selector will point to the state subtree managed by the particular component instance, rather than the global Redux state.
94+
> If you don't want a selector to be auto-scoped, don't put it in the selectors object. Instead set it as a property on the specification. This will bypass the auto-scoping behavior.
95+
96+
## Other properties
97+
98+
The specification's other properties will be `Object.assign()`ed onto the prototype for the class. Properties that are functions will be automatically bound to class instances by the constructor. Other properties will be available on the prototype.

docs/API/Components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Component Classes
44

5-
As in React, components are built up from the JavaScript object model. One creates a component class by passing a specification object to [createClass](createClass.md). To instantiate an instance of the class, which can then be mounted to a Redux state tree, use [createComponent](createComponent.md), which is the analogue of React's `createElement`:
5+
As in React, components are built up from the JavaScript object model. One creates a component class by passing a [specification object](ComponentSpecification.md) to [createClass](createClass.md). To instantiate an instance of the class, which can then be mounted to a Redux state tree, use [createComponent](createComponent.md), which is the analogue of React's `createElement`:
66
```coffeescript
77
componentInstance = createComponent(ComponentClass)
88
```

docs/API/SubtreeMixin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SubtreeMixin
22
```coffeescript
3-
SubtreeMixin = require 'redux-components/SubtreeMixin'
3+
{ SubtreeMixin } = require 'redux-components'
44
```
55

66
The `SubtreeMixin` is for creating components that manage a subtree of state. The state of the node is an object whose keys represent distinct subtrees. Each of these nodes has a redux-component instance mounted to it, and their reducers are combined via the standard Redux `combineReducers()` API to obtain the reducer for the parent node.

docs/API/createClass.md

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,6 @@
11
# createClass
22
```coffeescript
3-
createClass = require 'redux-components/createClass'
3+
{ createClass } = require 'redux-components'
44
createClass = ({spec}) -> ReduxComponentClass
55
```
6-
Creates a component class given a specification object. Generally speaking, any function assigned to a key on the specification object will be copied into the prototype of the class and thus become available as instance methods. The exception to this rule is for certain specially-named keys which are used by redux-components to implement core component behavior. The special keys are as follows:
7-
8-
## Meta properties
9-
10-
### spec.displayName
11-
```coffeescript
12-
spec.displayName = string
13-
```
14-
If specified, gives a name to this component that can be used by debugging and development tools.
15-
16-
> Just as with React, relying on displayName to do type checking in production is an antipattern. Use the instanceof operator.
17-
18-
### spec.statics
19-
```coffeescript
20-
spec.statics = { ... }
21-
```
22-
All keys and values from ```spec.statics``` are merged onto the constructor for the component class via ```Object.assign```. This makes them available as "static methods" on the class constructor.
23-
24-
### spec.mixins
25-
```coffeescript
26-
spec.mixins = [ {spec}, {spec}, ... ]
27-
```
28-
An array of additional specs that will be mixed into this spec before creating the class. Mixins are processed in the order they appear in the array. In general, the keys on the mixin specs will be assigned to the base spec as with ```Object.assign```, with auto-binding for functions. There is special behavior for certain keys. See [Mixins](Mixins.md) for more.
29-
30-
## Lifecycle methods
31-
32-
### spec.componentWillMount
33-
```coffeescript
34-
spec.componentWillMount = =>
35-
```
36-
### spec.componentDidMount
37-
```coffeescript
38-
spec.componentDidMount = =>
39-
```
40-
### spec.componentWillUnmount
41-
```coffeescript
42-
spec.componentWillUnmount = =>
43-
```
44-
Specifies the lifecycle methods for instances of the class. See [Components](Components.md) for details on when the methods are called.
45-
46-
## Redux-related properties
47-
48-
### spec.getReducer
49-
```coffeescript
50-
spec.getReducer = => (state, action) => nextState
51-
```
52-
getReducer returns a reducer function that will be used for the state subtree managed by this component.
53-
54-
Reducers made by getReducer are automatically bound to their component instances so that they can have access to scoped properties, particularly scoped action verbs.
55-
56-
> **NB:**
57-
> - After all mixins are evaluated, a final spec must have exactly one ```.getReducer```. We prefer to be unopinionated, so by default, there is no specified behavior for composing multiple reducers. (But see ```SubtreeMixin```.)
58-
59-
> - Do not use magic binding as an excuse to introduce impure behavior into your reducer! If you want the all the benefits of Redux, keep your reducers as pure functions of props and state. Don't make your reducer rely on non-constant properties of the redux-component, and don't be tempted to store any state on the redux-component itself. (You can sometimes use ```getReducer``` to mimic "impure" behaviors without making your reducer itself impure.)
60-
61-
> - By default, redux-components will call `getReducer()` only once when your component is mounted to a state tree. If you expect your reducer to change during the Redux store's lifecycle, you must arrange for `getReducer()` to be called at appropriate times, and for `Store.replaceReducer()` to be called on your root store. If you use redux-components and `SubtreeMixin` throughout your state tree, we provide facilities for automating this.
62-
63-
### spec.verbs
64-
```coffeescript
65-
spec.verbs = [ string, string, ... ]
66-
```
67-
Specifies a list of action names that will be scoped to each instance of the component using the instance's path in the state tree. Verbs are scoped according to the following pattern: ```#{path}:#{verb}``` and are available on the instance object at a key corresponding to the verb root. For example, a component instance mounted at ```foo.bar``` in the state tree having a verb ```"BAZ"``` would have the scoped verb ```"foo.bar:BAZ"``` available as ```this.BAZ```.
68-
69-
> If you don't want a verb to be scoped, don't put it in the verbs array. Instead set it as a plain string property on the specification. It will then bypass the auto-scoping behavior.
70-
71-
### spec.actionCreators
72-
```coffeescript
73-
spec.actionCreators = { key: (args...) -> action, ... }
74-
```
75-
Specify the action creators associated with this component class. Each property on the ```actionCreators``` object will be bound to each component instance and made available on the instance as a method.
76-
> **NB:** The redux-components core makes no assumptions about which middleware is present on a store. If you have e.g. `redux-thunk` middleware installed, you may of course return thunks from your actions and actionCreators.
77-
78-
### spec.actionDispatchers
79-
```coffeescript
80-
spec.actionDispatchers = { key: (args...) -> action, ... }
81-
```
82-
Specify actions associated with this component class. Action dispatchers are like action creators, except the value returned is automatically `dispatch()`ed to the Store where this component is mounted. The wrapped action function will return the same thing as `dispatch()`, allowing it to be used with thunks and other related middleware.
83-
84-
### spec.selectors
85-
```coffeescript
86-
spec.selectors = { key: (state, ...) -> any, ... }
87-
```
88-
Specify the selectors associated with this component class. Each property on the `selectors` object will be bound to each component instance and wrapped in a function that scopes the selector to the instance's state. The net effect will be that the state argument received by the selector will point to the state subtree managed by the particular component instance, rather than the global Redux state.
89-
> If you don't want a selector to be auto-scoped, don't put it in the selectors object. Instead set it as a property on the specification. This will bypass the auto-scoping behavior.
90-
91-
## Other properties
92-
93-
The specification's other properties will be `Object.assign()`ed onto the prototype for the class. Properties that are functions will be automatically bound to class instances by the constructor. Other properties will be available on the prototype.
6+
Creates a component class given a [component specification object](ComponentSpecification.md). Please see those docs for reference on how to create a specification object.

0 commit comments

Comments
 (0)