|
(function () { |
|
|
|
/** |
|
* Helper function which creates initial list with mock data |
|
*/ |
|
function getInitialList() { |
|
return _.range(10) |
|
.map(n => ({ |
|
id: n, |
|
title: `Item #${n}`, |
|
color: '#fff' |
|
})); |
|
} |
|
|
|
/** |
|
* Helper function which generates random value for the color component (red, green, blue) |
|
*/ |
|
function getRandomColorHexValue() { |
|
return Math.round(7 + 8 * Math.random()).toString(16); |
|
} |
|
|
|
/** |
|
* Dumb item component |
|
* It will print a message in console every time React invoke it |
|
*/ |
|
function ItemComponent({changeColor, item: {id, title, color}}) { |
|
console.log(`Render item: #${id}`); |
|
|
|
return React.createElement( |
|
'div', |
|
{ |
|
style: { |
|
backgroundColor: color, |
|
padding: '10px', |
|
borderTop: '1px solid #ddd', |
|
borderBottom: '1px solid #ddd', |
|
} |
|
}, |
|
React.createElement( |
|
'span', |
|
null, |
|
title |
|
), |
|
React.createElement( |
|
'button', |
|
{ |
|
onClick: changeColor, |
|
style: { |
|
marginLeft: '10px' |
|
} |
|
}, |
|
'Change color' |
|
) |
|
) |
|
} |
|
|
|
/** |
|
* Item enhancer to apply optimizations and action hanlers |
|
*/ |
|
const Item = Recompose.compose( |
|
// This HOC is basically controlled version of `Recompose.pure` or React.PureComponent |
|
Recompose.shouldUpdate((props, nextProps) => { |
|
if (!props.optimization || props.optimization !== nextProps.optimization) { |
|
return true; |
|
} |
|
|
|
return !Recompose.shallowEqual(props, nextProps); |
|
}), |
|
Recompose.withHandlers({ |
|
changeColor: props => () => { |
|
const {updateItem, item} = props; |
|
const r = getRandomColorHexValue(); |
|
const g = getRandomColorHexValue(); |
|
const b = getRandomColorHexValue(); |
|
|
|
return updateItem({ |
|
...item, |
|
color: `#${r}${g}${b}` |
|
}); |
|
} |
|
}), |
|
Recompose.mapProps(props => _.omit(props, ['updateItem'])) |
|
)(ItemComponent); |
|
|
|
/** |
|
* Dumb list component |
|
*/ |
|
function ListComponent({list, optimization, updateItem, updateOptimization}) { |
|
return React.createElement( |
|
'div', |
|
null, |
|
React.createElement( |
|
'button', |
|
{ |
|
onClick: () => { |
|
updateOptimization(!optimization); |
|
} |
|
}, |
|
optimization ? 'Disable optimization' : 'Enable optimization' |
|
), |
|
React.createElement( |
|
'div', |
|
{ |
|
style: { |
|
marginTop: '10px' |
|
} |
|
}, |
|
list.map(item => ( |
|
React.createElement(Item, {key: item.id, updateItem, item, optimization}) |
|
)) |
|
) |
|
); |
|
} |
|
|
|
/** |
|
* List enhancer which owns the state |
|
*/ |
|
const List = Recompose.compose( |
|
Recompose.withStateHandlers({ |
|
list: getInitialList(), |
|
optimization: false |
|
}, { |
|
updateList: () => list => ({list}), |
|
updateOptimization: () => optimization => ({optimization}) |
|
}), |
|
Recompose.withHandlers({ |
|
updateItem: props => nextItem => { |
|
const {list, updateList} = props; |
|
|
|
updateList(list.map(item => { |
|
return item.id === nextItem.id ? nextItem : item |
|
})); |
|
} |
|
}), |
|
Recompose.mapProps(props => _.omit(props, ['updateList'])) |
|
)(ListComponent); |
|
|
|
/** |
|
* Render the app |
|
*/ |
|
ReactDOM.render( |
|
React.createElement(List), |
|
document.getElementById('app') |
|
); |
|
|
|
}()); |