Skip to content

Commit

Permalink
Make generic component and reducer
Browse files Browse the repository at this point in the history
Note: some things are still broken, in particular actions requiring middleware.
I'm not sure how to fix them right now—we'll need to discuss this.
  • Loading branch information
gaearon committed Oct 3, 2015
1 parent 6a22288 commit a83002a
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 84 deletions.
22 changes: 16 additions & 6 deletions examples/counter/actions/list.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
export const ADD_COUNTER = 'ADD_COUNTER';
export const REMOVE_COUNTER = 'REMOVE_COUNTER';
export const ADD_TO_LIST = 'ADD_TO_LIST';
export const REMOVE_FROM_LIST = 'REMOVE_FROM_LIST';
export const PERFORM_IN_LIST = 'PERFORM_IN_LIST';

export function add() {
export function addToList() {
return {
type: ADD_COUNTER
type: ADD_TO_LIST
};
}

export function remove() {
export function removeFromList(index) {
return {
type: REMOVE_COUNTER
type: REMOVE_FROM_LIST,
index
};
}

export function performInList(index, action) {
return {
type: PERFORM_IN_LIST,
index,
action
};
}
16 changes: 7 additions & 9 deletions examples/counter/components/Counter.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import React, { Component, PropTypes } from 'react';
import { increment, incrementIfOdd, incrementAsync, decrement } from '../actions/counter';

class Counter extends Component {
render() {
const { increment, incrementIfOdd, incrementAsync, decrement, counter } = this.props;
const { dispatch, counter } = this.props;
return (
<p>
Clicked: {counter} times
{' '}
<button onClick={increment}>+</button>
<button onClick={() => dispatch(increment())}>+</button>
{' '}
<button onClick={decrement}>-</button>
<button onClick={() => dispatch(decrement())}>-</button>
{' '}
<button onClick={incrementIfOdd}>Increment if odd</button>
<button onClick={() => dispatch(incrementIfOdd())}>Increment if odd</button>
{' '}
<button onClick={() => incrementAsync()}>Increment async</button>
<button onClick={() => dispatch(incrementAsync())}>Increment async</button>
</p>
);
}
}

Counter.propTypes = {
increment: PropTypes.func.isRequired,
incrementIfOdd: PropTypes.func.isRequired,
incrementAsync: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
counter: PropTypes.number.isRequired
};

Expand Down
70 changes: 34 additions & 36 deletions examples/counter/components/List.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,39 @@
import React, { Component, PropTypes } from 'react';
import Counter from './Counter';
import { addToList, removeFromList, performInList } from '../actions/list';

class List extends Component {
export default function list(mapItemStateToProps) {
return function (Item) {
return class List extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
items: PropTypes.array.isRequired
};

increment(index) {
let action = this.props.counterActions.increment()
action.index = index
this.props.dispatch(action)
}
render() {
const { dispatch, items } = this.props;
return (
<div>
<button onClick={() =>
dispatch(addToList())
}>Add counter</button>

decrement(index) {
let action = this.props.counterActions.decrement()
action.index = index
this.props.dispatch(action)
}

render() {
const { add, remove, counterList, counterActions } = this.props;
return (
<p>
{' '}
<button onClick={add}>Add counter</button>
{' '}
<button onClick={remove}>Remove counter</button>
{counterList.map((counter, counterId) => {
return <Counter key={counterId} id={counterId} counter={counter} increment={this.increment.bind(this, counterId)} decrement={this.decrement.bind(this, counterId)}></Counter>
})}
</p>
);
}
<br />
{items.length > 0 &&
<button onClick={() =>
dispatch(removeFromList(items.length - 1))
}>Remove counter</button>
}
<br />
{this.props.items.map((item, index) =>
<Item {...mapItemStateToProps(item)}
key={index}
dispatch={action =>
dispatch(performInList(index, action))
} />
)}
</div>
)
}
}
};
}

List.propTypes = {
add: PropTypes.func.isRequired,
remove: PropTypes.func.isRequired,
counterList: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired
};

export default List;
25 changes: 11 additions & 14 deletions examples/counter/containers/App.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import List from '../components/List';
import * as CounterActions from '../actions/counter';
import * as ListActions from '../actions/list';

function mapStateToProps(state) {
import counter from '../components/Counter';
import list from '../components/list';

const CounterList = list(function mapItemStateToProps(itemState) {
return {
counterList: state.counterList
counter: itemState
};
}

function mapDispatchToProps(dispatch) {
let actions = bindActionCreators(ListActions, dispatch);
actions.counterActions = bindActionCreators(CounterActions, dispatch);
actions.dispatch = dispatch
return actions;
}
})(counter);

export default connect(mapStateToProps, mapDispatchToProps)(List);
export default connect(function mapStateToProps(state) {
return {
items: state.counterList
};
})(CounterList);
7 changes: 3 additions & 4 deletions examples/counter/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { combineReducers } from 'redux';
import counter from './counter';
import list from './list'

const counterList = list(counter);

const rootReducer = combineReducers({
counterList: list(counter, {
add: 'ADD_COUNTER',
remove: 'REMOVE_COUNTER'
})
counterList
});

export default rootReducer;
39 changes: 24 additions & 15 deletions examples/counter/reducers/list.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
export default function list(reducer, actionTypes) {
import { ADD_TO_LIST, REMOVE_FROM_LIST, PERFORM_IN_LIST } from '../actions/list';

export default function list(reducer) {
return function (state = [], action) {
const {
index,
action: innerAction
} = action;

switch (action.type) {
case actionTypes.add:
return [...state, reducer(undefined, action)];
case actionTypes.remove:
return [...state.slice(0, action.index), ...state.slice(action.index + 1)];
case ADD_TO_LIST:
return [
...state,
reducer(undefined, action)
];
case REMOVE_FROM_LIST:
return [
...state.slice(0, index),
...state.slice(index + 1)
];
case PERFORM_IN_LIST:
return [
...state.slice(0, index),
reducer(state[index], innerAction),
...state.slice(index + 1)
];
default:
const { index, ...rest } = action;
if (typeof index !== 'undefined') {
return state.map((item, i) => {
if(index == i) {
return reducer(item, rest)
} else {
return item
}
});
}
return state;
}
}
Expand Down

0 comments on commit a83002a

Please sign in to comment.