Skip to content

Commit

Permalink
Breaking API changes for 1.0
Browse files Browse the repository at this point in the history
Naming:

* “Stateless Stores” are now called reducers. (#137 (comment))
* The “Redux instance” is now called “The Store”. (#137 (comment))
* The dispatcher is removed completely. (#166 (comment))

API changes:

* <s>`composeStores`</s> is now `composeReducers`.
* <s>`createDispatcher`</s> is gone.
* <s>`createRedux`</s> is now `createStore`.
* `<Provider>` now accepts `store` prop instead of <s>`redux`</s>.
* The new `createStore` signature is `createStore(reducer: Function | Object, initialState: any, middlewares: Array | ({ getState, dispatch }) => Array)`.
* If the first argument to `createStore` is an object, `composeReducers` is automatically applied to it.
* The “smart” middleware signature changed. It now accepts an object instead of a single `getState` function. The `dispatch` function lets you “recurse” the middleware chain and is useful for async: #113 (comment).

Correctness changes:

* The `dispatch` provided by the default thunk middleware now walks the whole middleware chain.
* It is enforced now that raw Actions at the end of the middleware chain have to be plain objects.
* Nested dispatches are now handled gracefully. (#110)

Internal changes:

* The object in React context is renamed from <s>`redux`</s> to `store`.
* Some tests are rewritten for clarity, focus and edge cases.
* Redux in examples is now aliased to the source code for easier work on master.
  • Loading branch information
gaearon committed Jun 30, 2015
1 parent d3a31b2 commit 634d18e
Show file tree
Hide file tree
Showing 44 changed files with 791 additions and 475 deletions.
8 changes: 4 additions & 4 deletions examples/counter/containers/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react';
import CounterApp from './CounterApp';
import { createRedux } from 'redux';
import { createStore } from 'redux/index';
import { Provider } from 'redux/react';
import * as stores from '../stores';
import * as reducers from '../reducers';

const redux = createRedux(stores);
const store = createStore(reducers);

export default class App {
render() {
return (
<Provider redux={redux}>
<Provider store={store}>
{() => <CounterApp />}
</Provider>
);
Expand Down
2 changes: 1 addition & 1 deletion examples/counter/containers/CounterApp.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { bindActionCreators } from 'redux';
import { bindActionCreators } from 'redux/index';
import { connect } from 'redux/react';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/CounterActions';
Expand Down
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions examples/counter/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ module.exports = {
new webpack.NoErrorsPlugin()
],
resolve: {
alias: {
'redux': path.join(__dirname, '../../src')
},
extensions: ['', '.js']
},
module: {
Expand Down
11 changes: 6 additions & 5 deletions examples/todomvc/containers/App.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import React from 'react';
import TodoApp from './TodoApp';
import { createRedux } from 'redux';
import { createStore, composeReducers } from 'redux/index';
import { Provider } from 'redux/react';
import * as stores from '../stores';
import * as reducers from '../reducers';

const redux = createRedux(stores);
const reducer = composeReducers(reducers);
const store = createStore(reducer);

export default class App {
render() {
return (
<Provider redux={redux}>
{() => <TodoApp />}
<Provider store={store}>
{() => <TodoApp /> }
</Provider>
);
}
Expand Down
2 changes: 1 addition & 1 deletion examples/todomvc/containers/TodoApp.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { bindActionCreators } from 'redux';
import { bindActionCreators } from 'redux/index';
import { Connector } from 'redux/react';
import Header from '../components/Header';
import MainSection from '../components/MainSection';
Expand Down
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions examples/todomvc/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ module.exports = {
new webpack.NoErrorsPlugin()
],
resolve: {
alias: {
'redux': path.join(__dirname, '../../src')
},
extensions: ['', '.js']
},
module: {
Expand Down
53 changes: 0 additions & 53 deletions src/Redux.js

This file was deleted.

50 changes: 50 additions & 0 deletions src/Store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import invariant from 'invariant';
import isPlainObject from './utils/isPlainObject';

export default class Store {
constructor(reducer, initialState) {
invariant(
typeof reducer === 'function',
'Expected the reducer to be a function.'
);

this.state = initialState;
this.listeners = [];
this.replaceReducer(reducer);
}

getReducer() {
return this.reducer;
}

replaceReducer(nextReducer) {
this.reducer = nextReducer;
this.dispatch({ type: '@@INIT' });
}

dispatch(action) {
invariant(
isPlainObject(action),
'Actions must be plain objects. Use custom middleware for async actions.'
);

const { reducer } = this;
this.state = reducer(this.state, action);
this.listeners.forEach(listener => listener());
return action;
}

getState() {
return this.state;
}

subscribe(listener) {
const { listeners } = this;
listeners.push(listener);

return function unsubscribe() {
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
}
12 changes: 6 additions & 6 deletions src/components/createConnector.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import createReduxShape from '../utils/createReduxShape';
import createStoreShape from '../utils/createStoreShape';
import identity from '../utils/identity';
import shallowEqual from '../utils/shallowEqual';
import isPlainObject from '../utils/isPlainObject';
import invariant from 'invariant';

export default function createConnector(React) {
const { Component, PropTypes } = React;
const storeShape = createStoreShape(PropTypes);

return class Connector extends Component {
static contextTypes = {
redux: createReduxShape(PropTypes).isRequired
store: storeShape.isRequired
};

static propTypes = {
Expand Down Expand Up @@ -38,12 +39,11 @@ export default function createConnector(React) {

constructor(props, context) {
super(props, context);

this.state = this.selectState(props, context);
}

componentDidMount() {
this.unsubscribe = this.context.redux.subscribe(::this.handleChange);
this.unsubscribe = this.context.store.subscribe(::this.handleChange);
}

componentWillReceiveProps(nextProps) {
Expand All @@ -63,7 +63,7 @@ export default function createConnector(React) {
}

selectState(props, context) {
const state = context.redux.getState();
const state = context.store.getState();
const slice = props.select(state);

invariant(
Expand All @@ -78,7 +78,7 @@ export default function createConnector(React) {
render() {
const { children } = this.props;
const { slice } = this.state;
const { redux: { dispatch } } = this.context;
const { store: { dispatch } } = this.context;

return children({ dispatch, ...slice });
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/createProvideDecorator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import getDisplayName from '../utils/getDisplayName';
export default function createProvideDecorator(React, Provider) {
const { Component } = React;

return function provide(redux) {
return function provide(store) {
return DecoratedComponent => class ProviderDecorator extends Component {
static displayName = `Provider(${getDisplayName(DecoratedComponent)})`;
static DecoratedComponent = DecoratedComponent;

render() {
return (
<Provider redux={redux}>
<Provider store={store}>
{() => <DecoratedComponent {...this.props} />}
</Provider>
);
Expand Down
28 changes: 13 additions & 15 deletions src/components/createProvider.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import createReduxShape from '../utils/createReduxShape';
import createStoreShape from '../utils/createStoreShape';

export default function createProvider(React) {
const { Component, PropTypes } = React;

const reduxShapeIsRequired = createReduxShape(PropTypes).isRequired;
const storeShape = createStoreShape(PropTypes);

return class Provider extends Component {
static propTypes = {
redux: reduxShapeIsRequired,
children: PropTypes.func.isRequired
static childContextTypes = {
store: storeShape.isRequired
};

static childContextTypes = {
redux: reduxShapeIsRequired
static propTypes = {
children: PropTypes.func.isRequired
};

getChildContext() {
return { redux: this.state.redux };
return { store: this.state.store };
}

constructor(props, context) {
super(props, context);
this.state = { redux: props.redux };
this.state = { store: props.store };
}

componentWillReceiveProps(nextProps) {
const { redux } = this.state;
const { redux: nextRedux } = nextProps;
const { store } = this.state;
const { store: nextStore } = nextProps;

if (redux !== nextRedux) {
const nextDispatcher = nextRedux.getDispatcher();
redux.replaceDispatcher(nextDispatcher);
if (store !== nextStore) {
const nextReducer = nextStore.getReducer();
store.replaceReducer(nextReducer);
}
}

Expand Down
26 changes: 0 additions & 26 deletions src/createDispatcher.js

This file was deleted.

13 changes: 0 additions & 13 deletions src/createRedux.js

This file was deleted.

45 changes: 45 additions & 0 deletions src/createStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Store from './Store';
import composeReducers from './utils/composeReducers';
import composeMiddleware from './utils/composeMiddleware';
import thunkMiddleware from './middleware/thunk';

const defaultMiddlewares = ({ dispatch, getState }) => [
thunkMiddleware({ dispatch, getState })
];

export default function createStore(
reducer,
initialState,
middlewares = defaultMiddlewares
) {
const finalReducer = typeof reducer === 'function' ?
reducer :
composeReducers(reducer);

const store = new Store(finalReducer, initialState);
const getState = ::store.getState;

const rawDispatch = ::store.dispatch;
let cookedDispatch = null;

function dispatch(action) {
return cookedDispatch(action);
}

const finalMiddlewares = typeof middlewares === 'function' ?
middlewares({ dispatch, getState }) :
middlewares;

cookedDispatch = composeMiddleware(
...finalMiddlewares,
rawDispatch
);

return {
dispatch: cookedDispatch,
subscribe: ::store.subscribe,
getState: ::store.getState,
getReducer: ::store.getReducer,
replaceReducer: ::store.replaceReducer
};
}
Loading

0 comments on commit 634d18e

Please sign in to comment.