Skip to content

Instantly share code, notes, and snippets.

@rolandcoops
Last active August 19, 2019 09:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rolandcoops/48c9ecf603cd20150539c211747bd1e3 to your computer and use it in GitHub Desktop.
Save rolandcoops/48c9ecf603cd20150539c211747bd1e3 to your computer and use it in GitHub Desktop.
Normalized redux structure example (see https://stackoverflow.com/a/37266130/8602065)
import React from 'react'
import { connect } from 'react-redux'
/**
* Item Component
*/
const Item = ({ item }) => (
<li className="item">
<div>
<span className="item__name">{item.name}</span>
<span className="item__type">{item.type}</span>
</div>
<div className="item__description">{item.description}</div>
</li>
)
Item.propTypes = {
itemId: PropTypes.string.isRequired, // used once in mapStateToPropsFactory
item: PropTypes.object.isRequired,
}
// provides memoization for component instance based on initialOwnProps
const mapStateToPropsFactory = (initialState, initialOwnProps) =>
(state) => ({
item: state.items[initialOwnProps.itemId]
})
export default connect(mapStateToPropsFactory)(ItemList)
import actions from 'actions/ItemActionCreators'
/**
* Reducer (partial)
*/
const initialState = {
items: {}, // normalized data of shape { id1: {…}, id2, {…}, …}
itemIds: [], // keys of normalized data [id1, id2, …]
}
// waaay faster than _.omit, see:
// https://github.com/FormidableLabs/victory/issues/956#issuecomment-377567239
const omit = (items, itemId ) => {
const { [itemId]: _omit, ...nextItems } = items
return nextItems
}
const handlers = {
/* Item list populator */
[actions.GET_ITEMS_RECEIVE]: (state, { items }) =>
({...state,
items: action.items,
itemIds: Object.keys(action.items),
}),
/* Item CRUD operations */
[actions.CREATE_ITEM_RECEIVE]: (state, { item }) =>
({...state,
items: { ...state.items, [item.id]: item },
}),
[actions.UPDATE_ITEM_RECEIVE]: (state, { item }) =>
({...state,
items: { ...state.items, [item.id]: item },
}),
[actions.DELETE_ITEM_RECEIVE]: (state, { item }) => {
const nextItems = omit(state.items, item.id)
return {...state,
items: nextItems,
itemIds: Object.keys(nextItems),
}
},
}
export default (reducer) =>
(state = initialState, action) =>
reducer(handlers, state, action)
import React from 'react'
import { connect } from 'react-redux'
import { compose } from 'redux'
import Item from 'components/Item'
/**
* List Component
*/
const ItemList = ({ itemIds }) => (
<ol className="item-list">
{itemIds.map((id) => (
<Item key={id} itemId={id} />
))}
</ol>
)
ItemList.propTypes = {
itemIds: PropTypes.arrayOf(PropTypes.string).isRequired,
}
const mapStateToProps = (state) => ({
itemIds: state.itemIds,
})
export default compose(
connect(mapStateToProps),
React.memo,
)(ItemList)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment