Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept stores as an object of prop key to store #31

Merged
merged 1 commit into from
Jun 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ import {

// but you can write this part anyhow you like:

const initialState = { counter: 0 };
const initialState = 0;

function increment({ counter }) {
return { counter: counter + 1 };
function increment(counter) {
return counter + 1;
}

function decrement({ counter }) {
return { counter: counter - 1 };
function decrement(counter) {
return counter - 1;
}

// what's important is that Store is a pure function too
Expand Down Expand Up @@ -156,14 +156,13 @@ import Counter from './Counter';

export default class CounterContainer {
render() {
// stores must be an array.
// actions must be a string -> function map.
// stores and actions must both be string -> function maps.
// props passed to children will combine these actions and state.
return (
<Container stores={[counterStore]}
<Container stores={{ counter: stores.counterStore }}
actions={{ increment, decrement }}>
{/* Yes this is a function as a child. Bear with me. */}
{props => <Counter {...props} />}
{({ state, actions }) => <Counter {...state} {...actions} />}
</Container>
);
}
Expand All @@ -183,7 +182,7 @@ import counterStore from './stores/counterStore';

@container({
actions: { increment, decrement },
stores: [counterStore]
stores: { counter: counterStore }
})
export default class Counter {
static propTypes = {
Expand Down
4 changes: 2 additions & 2 deletions examples/counter/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import Counter from './Counter';
export default class CounterApp extends Component {
render() {
return (
<Container stores={[stores.counterStore]}
<Container stores={{ counter: stores.counterStore }}
actions={{ increment, decrement }}>
{props => <Counter {...props} />}
{({ state, actions }) => <Counter {...state} {...actions} />}
</Container>
);
}
Expand Down
10 changes: 5 additions & 5 deletions examples/counter/stores/counterStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {
DECREMENT_COUNTER
} from '../constants/ActionTypes';

const initialState = { counter: 0 };
const initialState = 0;

function increment({ counter }) {
return { counter: counter + 1 };
function increment(counter) {
return counter + 1;
}

function decrement({ counter }) {
return { counter: counter - 1 };
function decrement(counter) {
return counter - 1;
}

export default function counterStore(state = initialState, action) {
Expand Down
2 changes: 1 addition & 1 deletion examples/todo/Body.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { container } from 'redux';
import { todoStore } from './stores/index';

@container({
stores: [todoStore]
stores: { todos: todoStore }
})
export default class Body {
static propTypes = {
Expand Down
20 changes: 8 additions & 12 deletions examples/todo/stores/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { ADD_TODO } from '../constants/ActionTypes';

const initialState = {
todos: [{
text: 'do something',
id: 0
}]
};
const initialState = [{
text: 'do something',
id: 0
}];

export function todoStore(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {
todos: [{
id: state.todos[0].id + 1,
text: action.text
}].concat(state.todos)
};
return [{
id: state[0].id + 1,
text: action.text
}].concat(state);
}

return state;
Expand Down
29 changes: 16 additions & 13 deletions src/Container.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, PropTypes } from 'react';
import values from 'lodash/object/values';
import mapValues from 'lodash/object/mapValues';
import identity from 'lodash/utility/identity';
import invariant from 'invariant';
import isPlainObject from 'lodash/lang/isPlainObject';

Expand All @@ -12,11 +12,11 @@ export default class ReduxContainer extends Component {
static propTypes = {
children: PropTypes.func.isRequired,
actions: PropTypes.object.isRequired,
stores: PropTypes.arrayOf(PropTypes.func.isRequired).isRequired
stores: PropTypes.object.isRequired
}

static defaultProps = {
stores: [],
stores: {},
actions: {}
};

Expand All @@ -42,23 +42,26 @@ export default class ReduxContainer extends Component {
'"actions" must be a plain object with functions as values. Did you misspell an import?'
);
invariant(
Array.isArray(stores) &&
stores.every(s => typeof s === 'function'),
'"stores" must be an array of functions. Did you misspell an import?'
isPlainObject(stores) &&
Object.keys(stores).every(key => typeof stores[key] === 'function'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addind:

import every from 'lodash/collection/every';
import isFunction from 'lodash/lang/isFunction';

and changing the test to:

isPlainObject(stores) && every(stores, isFunction)

Would make code more readable.

'"stores" must be a plain object with functions as values. Did you misspell an import?'
);

const { wrapActionCreator, observeStores, getStoreKey } = this.context.redux;
const { wrapActionCreator, observeStores } = this.context.redux;
this.actions = mapValues(props.actions, wrapActionCreator);

if (this.unsubscribe) {
this.unsubscribe();
}

this.mapState = (stores.length === 1) ?
state => state[getStoreKey(stores[0])] :
identity;
this.unsubscribe = observeStores(values(stores), this.handleChange);
}

this.unsubscribe = observeStores(stores, this.handleChange);
mapState(stateFromStores) {
const { getStoreKey } = this.context.redux;
return mapValues(this.props.stores, store =>
stateFromStores[getStoreKey(store)]
);
}

handleChange(stateFromStores) {
Expand All @@ -72,8 +75,8 @@ export default class ReduxContainer extends Component {

render() {
return this.props.children({
...this.actions,
...this.state
state: this.state,
actions: this.actions
});
}
}
12 changes: 10 additions & 2 deletions src/addons/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ import React from 'react';
import Container from '../Container';
import getDisplayName from './getDisplayName';

export default function container({ actions, stores }) {
function defaultTransformProps({ state, actions }) {
return { ...state, ...actions };
}

export default function container(
{ actions, stores },
transformProps = defaultTransformProps
) {
return (DecoratedComponent) => class ReduxContainerDecorator {
static displayName = `ReduxContainer(${getDisplayName(DecoratedComponent)})`;

render() {
return (
<Container actions={actions} stores={stores}>
{props => <DecoratedComponent {...this.props} {...props} />}
{props => <DecoratedComponent {...this.props}
{...transformProps(props)} />}
</Container>
);
}
Expand Down