Skip to content

Commit 532672e

Browse files
committed
Don't use getAtom(), pass state on each dispatch instead
1 parent 38f4a16 commit 532672e

File tree

8 files changed

+142
-37
lines changed

8 files changed

+142
-37
lines changed

examples/components/Transactions.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React, { PropTypes } from 'react';
2+
import DOMify from 'react-domify';
3+
4+
export default class Transactions {
5+
static propTypes = {
6+
status: PropTypes.object.isRequired,
7+
commit: PropTypes.func.isRequired,
8+
begin: PropTypes.func.isRequired,
9+
rollback: PropTypes.func.isRequired
10+
};
11+
12+
render() {
13+
const { status, commit, begin, rollback } = this.props;
14+
return (
15+
<p>
16+
<DOMify value={status} />
17+
{' '}
18+
<button onClick={begin}>Begin</button>
19+
{' '}
20+
<button onClick={commit}>Commit</button>
21+
{' '}
22+
<button onClick={rollback}>Rollback</button>
23+
</p>
24+
);
25+
}
26+
}

examples/containers/App.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import { createDispatcher, Provider, composeStores, compose } from 'redux';
33
import CounterApp from './CounterApp';
44
import TodoApp from './TodoApp';
5+
import TransactionsApp from './TransactionsApp';
56
import * as stores from '../stores/index';
67
import createTransactor from '../middleware/createTransactor';
78
import callbackMiddleware from 'redux/middleware/callback';
@@ -17,11 +18,16 @@ function promiseMiddleware(next) {
1718

1819
const store = composeStores(stores);
1920
const transactor = createTransactor();
20-
const dispatcher = createDispatcher(getAtom => compose(
21-
promiseMiddleware,
22-
callbackMiddleware,
23-
transactor(getAtom, store)
24-
));
21+
// const dispatcher = createDispatcher(compose(
22+
// promiseMiddleware,
23+
// callbackMiddleware,
24+
// transactor(store)
25+
// ));
26+
const dispatcher = createDispatcher({
27+
middleware: compose(promiseMiddleware, callbackMiddleware),
28+
reducer: transactor(store)
29+
});
30+
2531

2632
global.transactor = transactor;
2733

@@ -33,6 +39,7 @@ export default class App {
3339
<div>
3440
<CounterApp />
3541
<TodoApp />
42+
<TransactionsApp transactor={transactor} />
3643
</div>
3744
}
3845
</Provider>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, { PropTypes } from 'react';
2+
import { Connector } from 'redux';
3+
import Transactions from '../components/Transactions';
4+
5+
const transactorShape = PropTypes.shape({
6+
getStatus: PropTypes.func.isRequired,
7+
begin: PropTypes.func.isRequired,
8+
commit: PropTypes.func.isRequired,
9+
rollback: PropTypes.func.isRequired
10+
});
11+
12+
export default class TransactionsApp {
13+
static propTypes = {
14+
transactor: transactorShape.isRequired
15+
};
16+
17+
render() {
18+
return (
19+
// TODO: Need a way to subscribe to all dispatches, without memoization
20+
<Connector select={() => ({ lol: {} })}>
21+
{::this.renderChild}
22+
</Connector>
23+
);
24+
}
25+
26+
renderChild() {
27+
const { transactor: { getStatus, begin, commit, rollback } } = this.props;
28+
29+
return (
30+
<Transactions status={getStatus()}
31+
commit={commit}
32+
begin={begin}
33+
rollback={rollback} />
34+
);
35+
}
36+
}

examples/middleware/createTransactor.js

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
1+
function noop() {}
2+
13
class Transactor {
24
inProgress = false;
35
actions = [];
46

5-
middleware(getAtom, store) {
7+
middleware(store) {
68
this.store = store;
7-
this.getAtom = getAtom;
8-
this.headState = getAtom();
9-
10-
return next => {
11-
this.next = next;
12-
13-
return action => {
14-
if (this.inProgress) {
15-
this.actions.push(action);
16-
return next(this.reduceActions(this.headState, this.actions));
17-
} else {
18-
const state = this.store(this.getAtom(), action);
19-
this.headState = state;
20-
return next(state);
21-
}
9+
10+
return (state, dispatch) => {
11+
this.dispatch = dispatch;
12+
this.currentState = state;
13+
14+
if (this.isCapturingState) {
15+
this.isCapturingState = false;
16+
return () => () => this.next(state);
17+
}
18+
19+
return next => {
20+
this.next = next;
21+
22+
return action => {
23+
if (this.inProgress) {
24+
this.actions.push(action);
25+
const nextState = this.reduceActions(this.headState, this.actions);
26+
return this.next(nextState);
27+
} else {
28+
const nextState = this.store(this.currentState, action);
29+
return this.next(nextState);
30+
}
31+
};
2232
};
2333
};
2434
}
2535

36+
// Get the current state atom by running a dummy dispatch.
37+
getCurrentState() {
38+
this.isCapturingState = true;
39+
this.dispatch();
40+
return this.currentState;
41+
}
42+
2643
reduceActions(initialState, actions) {
2744
return actions.reduce(
2845
(state, action) => this.store(state, action),
@@ -31,24 +48,30 @@ class Transactor {
3148
}
3249

3350
begin() {
34-
this.inProgress = true;
35-
this.dirty = false;
36-
this.actions = [];
51+
if (!this.inProgress) {
52+
this.inProgress = true;
53+
this.actions = [];
54+
this.headState = this.getCurrentState();
55+
}
3756
}
3857

3958
commit() {
40-
this.inProgress = false;
41-
this.dirty = false;
42-
this.actions = [];
43-
this.headState = this.getAtom();
59+
if (this.inProgress) {
60+
this.inProgress = false;
61+
this.actions = [];
62+
delete this.headState;
63+
this.next(this.getCurrentState());
64+
}
4465
}
4566

4667
rollback() {
47-
const { headState } = this;
48-
this.inProgress = false;
49-
this.dirty = false;
50-
this.actions = [];
51-
return this.next(headState);
68+
if (this.inProgress) {
69+
this.inProgress = false;
70+
this.actions = [];
71+
this.currentState = this.headState;
72+
delete this.headState;
73+
return this.next(this.currentState);
74+
}
5275
}
5376

5477
getStatus() {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"babel-loader": "^5.1.2",
3939
"eslint": "^0.22.1",
4040
"eslint-plugin-react": "^2.3.0",
41+
"react-domify": "~0.2.1",
4142
"react-hot-loader": "^1.2.7",
4243
"rimraf": "^2.3.4",
4344
"webpack": "^1.9.6",

src/Dispatcher.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import compose from './utils/compose';
2+
13
export default class Dispatcher {
2-
constructor(middleware) {
3-
this.middleware = middleware(::this.getAtom);
4+
constructor({ reducer, middleware }) {
5+
this.reducer = reducer;
6+
this.middleware = middleware;
47
this.initialize();
58
}
69

@@ -17,8 +20,12 @@ export default class Dispatcher {
1720
return { atom, subscriptions };
1821
}
1922

20-
dispatch(action) {
21-
this.middleware(nextAtom => this.setAtom(nextAtom))(action);
23+
dispatch = (action) => {
24+
this.middleware(
25+
_action => this.reducer(this.getAtom(), this.dispatch)(
26+
nextAtom => this.setAtom(nextAtom)
27+
)(_action)
28+
)(action);
2229
}
2330

2431
getAtom() {

src/createReducer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default function createReducer(store) {
2+
return state => next => action =>
3+
next(store(state, action));
4+
}

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Core
22
export createDispatcher from './createDispatcher';
3+
export createReducer from './createReducer';
34

45
// Wrapper components
56
export Provider from './components/Provider';

0 commit comments

Comments
 (0)