Skip to content

Commit b226a62

Browse files
committed
add README, update package description
Description: - Add README.md - update description value in package.json
1 parent f78466f commit b226a62

File tree

2 files changed

+244
-1
lines changed

2 files changed

+244
-1
lines changed

README.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
React Redux Connector
2+
=====================
3+
4+
Object-oriented React bindings for [Redux](https://github.com/reactjs/redux).
5+
6+
## Installation
7+
8+
```
9+
npm install --save react-redux-connector
10+
```
11+
12+
## Requirements
13+
14+
Currently, `react-redux-connector` is available only as [npm](http://npmjs.com/) package.
15+
To effectively work with the library, you will also have to use transpiler like
16+
[babel](https://babeljs.io/) with [es2015-classes](https://babeljs.io/docs/plugins/transform-es2015-classes)
17+
and [class-properties](https://babeljs.io/docs/plugins/transform-class-properties) features enabled.
18+
19+
## Motivation
20+
21+
Pretty quickly after starting using `react-redux` I started to feel uncomfortability of having
22+
my actions, reducer definitions and component code that actually uses former scattered among
23+
different files and places in the application. Also, I didn't like nor huge switch-case
24+
reducer functions, nor lots of same-looking small reducer functions each of which
25+
accepts repetitive (state, action) arguments pair. As a Rails developer, I felt a need of
26+
some kind of separate abstraction layer that will hoist all the redux-specific logic
27+
in a declarative and DRY way. That's how the concept of redux `Connector` component was born.
28+
29+
## Documentation
30+
31+
`react-redux-connector` is inspired by Dan Abramov's awesome [react-redux](https://github.com/reactjs/react-redux)
32+
library. At it's lowest level it uses react-redux's subscription mechanism to
33+
redux store (this part of code was ported from react-redux), however, `react-redux-connector`
34+
provides completely different way of organizing react-related logic of your
35+
application and it's usage. `react-redux-connector` exports `Connector`, `Connection`
36+
and `Reductor` classes, the most important of which is, unsuprisingly, the `Connector` class.
37+
38+
### Connector
39+
40+
Each Connector hoists all react-related logic (reducer functions, actions,
41+
dispatching, etc) and provides bindings for your React components (which are
42+
called connections).
43+
44+
#### API
45+
46+
Connector classes should have following properties set up:
47+
48+
- `static $connection` - the React component (view layer) that Connector provides
49+
connections for.
50+
51+
- `static $state` - the initial redux state that will be used in reducer function.
52+
53+
- `static $namespace` - the path of connector's `$state` in full redux's state.
54+
Should be something like `'profile.show'` or `'todos.list'`. It is also used
55+
for generation of action types (which will look like `'profile.show/$receive'`).
56+
This property is set automatically in `Connector.reduce` function that generates
57+
required `$reducer` function (see bellow). But if connector only provides data
58+
with no action handling, appropriate `$namespace` should be set explicitly.
59+
Defaults to `'global'`.
60+
61+
- `static $reducer` - a reducer function that should be generated with `Connector.reduce`
62+
function. **This function should be called on behalf of your connector**, i.e.
63+
`static $reducer = TodosConnector.reduce(...)`. This function should be called like this:
64+
65+
```js
66+
static $reducer = YourConnector.reduce('your.namespace', (state) => ({
67+
$actionOne: (arg) => newState,
68+
$otherAction: (arg1, arg2) => anotherNewState
69+
}));
70+
```
71+
72+
By calling `reduce` in this way, your connector's prototype gets `$$actionOne` and
73+
`$$otherAction` methods that will **dispatch** corresponding action that will
74+
trigger corresponding reducer's code with arguments you've passed.
75+
76+
- `$expose` instance method that should return object that will be passed
77+
to $connection component in props. Yes, you can think of it as react-redux's
78+
`mapStateToProps`. This method accepts 2 arguments: `$state` which is
79+
current connector's state (i.e. part of the full state under connector's namespace)
80+
and `state`, which is full redux state. Defaults to function that simply returns `$state`.
81+
82+
#### $-functions
83+
84+
All functions in connector's prototype that start with *exactly one* '$' sign
85+
will be available in connector's connection component and all other nested
86+
connection components.
87+
88+
#### Example
89+
90+
```js
91+
import { Connector } from 'react-redux-connector';
92+
import Todos from './Todos';
93+
import { get, post, put, destroy } from 'utils';
94+
95+
export default class TodosConnector extends Connector {
96+
static $connection = Todos;
97+
static $state = [];
98+
static $reducer = TodosConnector.reduce('todos', (state) => ({
99+
$receive: (items) => items,
100+
$addItem: (item) => [...state, item],
101+
$updateItem: (item) => state.map(i => i.id === item.id ? item : i),
102+
$removeItem: (id) => state.filter(i => i.id !== id)
103+
}));
104+
105+
$expose($state) {
106+
return { items: $state };
107+
}
108+
109+
$load() {
110+
return get('/todos')
111+
.then(response => this.$$receive(response.data));
112+
}
113+
114+
$create(item) {
115+
return post('/todos', { item })
116+
.then(response => this.$$addItem(response.data));
117+
}
118+
119+
$update(item) {
120+
return put(`/todos/${item.id}`, { item })
121+
.then(response => this.$$updateItem(response.data));
122+
}
123+
124+
$destroy(id) {
125+
return destroy(`/todos/${id}`)
126+
.then(() => this.$$removeItem(id));
127+
}
128+
}
129+
```
130+
131+
### Connection
132+
133+
Connection is a very simple helper object that you should inherit from instead of
134+
`React.Component`. Connection components can call all $-starting methods that
135+
are defined in Connector (that intended to result in dispatching an action).
136+
137+
Note that only explicitly connected connection (i.e. the one that is specified
138+
in connector's $connection property) gets connector's exposed state in properties.
139+
All other connections nested under that 'main' one have usual props that you've
140+
passed to them via React means, but they **do** have access to connector's $-functions.
141+
142+
#### Example
143+
144+
```js
145+
import { Connection } from 'react-redux-connector';
146+
147+
export default class Todos extends Connection {
148+
state = { title: '' };
149+
150+
componentDidMount() {
151+
this.$load();
152+
}
153+
154+
saveItem() {
155+
this.$create({ title: this.state.title })
156+
.then(() => this.setState({ title: '' }));
157+
}
158+
159+
destroyItem(id) {
160+
this.$destroy(id);
161+
}
162+
163+
render() {
164+
return (
165+
<div>
166+
{this.props.items.map(item =>
167+
<div key={item.id}>
168+
{item.title}
169+
<button onClick={() => this.destroyItem(item.id)}>Delete</button>
170+
</div>
171+
)}
172+
<input onChange={(e) => this.setState({ title: e.target.value })} />
173+
<button onClick={() => this.saveItem()}>Save</button>
174+
</div>
175+
);
176+
}
177+
}
178+
```
179+
180+
**NOTE:** if you don't want to inherit from `Connection`, you can gain access to
181+
connector's $-functions using react context:
182+
183+
```js
184+
class Todos extends Component {
185+
static contextTypes = {
186+
on: PropTypes.object
187+
};
188+
189+
componentDidMount() {
190+
this.context.on.$load();
191+
}
192+
193+
// ...
194+
}
195+
```
196+
197+
Actually, that's only one thing Connection does - provides a kind of syntactic
198+
sugar for calling connector's $-functions.
199+
200+
### Reductor
201+
202+
Reductor is a special helper component that acts as react-redux's state provider,
203+
but it's main purpose is to generate a store reducer function for you. On initialization
204+
it will traverse all children tree looking for connectors, collect them and use
205+
to create and provide store with given createStore prop. Connectors that are not
206+
present in the children tree should be listed in `connectors` property. Naturally,
207+
the most obvious example is usage with [react-router](https://www.npmjs.com/package/react-router)
208+
routes.
209+
210+
```js
211+
import { Reductor } from 'react-redux-connector';
212+
import { createStore } from 'redux';
213+
214+
// bellow are Connector components imported
215+
import Profile from 'application/profile';
216+
import { Todos, TodoDetails } from 'application/todos';
217+
218+
export default class Routes extends Component {
219+
return (
220+
<Reductor createStore={createStore}>
221+
<Router history={history}>
222+
<IndexRedirect to="/profile" />
223+
<Route path="/profile" component={Profile} />
224+
<Route path="/todos" component={Todos} />
225+
<Route path="/todos/:id" component={Todos} />
226+
</Router>
227+
</Reductor>
228+
);
229+
}
230+
```
231+
232+
#### Reductor Props
233+
234+
| Prop Name | Spec | Description |
235+
|-----------------|----------------------------------------------------------|-------------|
236+
| `createStore` | **required** `PropTypes.func` | A function that takes reducer function (generated by a Reductor) as an argument and returns a redux state |
237+
| `connectors` | *optional* `PropTypes.arrayOf(PropTypes.func)` | An array of connectors that cannot be mentioned in children tree, but whose `$reducer` functions should become a part of generated redux reducer function |
238+
| `connectorProp` | *optional* `PropTypes.string`, defaults to `'component'` | A prop of components in children tree that contains a Connector as a value |
239+
240+
241+
## License
242+
243+
MIT

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "react-redux-connector",
33
"version": "1.0.0",
4-
"description": "Connector component to hoist all Redux-related logic of your app",
4+
"description": "Object-oriented React binding for Redux",
55
"main": "./lib/index.js",
66
"scripts": {
77
"build": "babel src --out-dir lib",

0 commit comments

Comments
 (0)