Skip to content

Instantly share code, notes, and snippets.

@kettanaito
Last active March 28, 2018 15:12
Show Gist options
  • Save kettanaito/f000f1b550b985598ff57715ad9d6e36 to your computer and use it in GitHub Desktop.
Save kettanaito/f000f1b550b985598ff57715ad9d6e36 to your computer and use it in GitHub Desktop.
React motion issue
import { fromJS } from 'immutable';
import initialState from './initial-state';
import * as actions from './actions';
import { getTotalPrice } from './selectors';
export default function cart(state = initialState, action) {
switch (action.type) {
case actions.ADD_ITEM: {
const nextItems = state.get('items').push(fromJS(action.item));
return state.set('items', nextItems);
}
case actions.REMOVE_ITEM: {
const nextItems = state.get('items').filterNot(item => (item.get('id') === action.id));
return state.set('items', nextItems);
}
default: {
return state;
}
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { connect } from 'react-redux';
import { addItem, removeItem } from '@state/cart/actions';
import { getTotalPrice } from '@state/cart/selectors';
import { Grid, Box, Button } from '@client/components';
/* Components */
import CartItemsList from './components/CartItemsList';
class Cart extends React.Component {
static propTypes = {
totalPrice: PropTypes.number.isRequired
}
handleAddItem = () => {
this.props.addItem({
item: {
id: Math.random().toString(),
album: 'Yesterday',
bandName: 'Beatles',
year: 1999,
genre: 'Rock',
imageUrl: 'https://images.8tracks.com/cover/i/000/504/274/The-Beatles-the-beatles-9709058-1600-1200-4584.jpg?rect=330,0,1200,1200&q=98&fm=jpg&fit=max&w=320&h=320',
price: {
net: 9.99,
gross: 11.99
}
}
});
}
componentWillReceiveProps(nextProps) {
console.log({ nextProps });
}
handleRemoveItem = (id) => {
this.props.removeItem({ id });
}
render() {
const { items, totalPrice } = this.props;
const itemsCount = items.size;
return (
<React.Fragment>
<Helmet>
<title>Cart</title>
</Helmet>
<Grid>
<h1>Cart</h1>
<h2>Items ({ itemsCount })</h2>
<CartItemsList
items={ items }
onRemoveItem={ this.handleRemoveItem } />
<h2>Summary</h2>
<p>Total price: { totalPrice }</p>
<Box spread>
<Button onClick={ this.handleAddItem }>Add item</Button>
<Button>Checkout</Button>
</Box>
</Grid>
</React.Fragment>
);
}
}
export default connect(state => ({
items: state.getIn(['cart', 'items']),
totalPrice: getTotalPrice(state.get('cart'))
}), {
addItem,
removeItem
})(Cart);
import React from 'react';
import PropTypes from 'prop-types';
import { List } from 'immutable';
import { spring, presets, TransitionMotion } from 'react-motion';
/* Components */
import CartItem from './CartItem';
export default class CartItemsList extends React.Component {
static propTypes = {
items: PropTypes.instanceOf(List).isRequired
}
getDefaultStyles = () => {
return this.props.items.reduce((acc, item) => {
return acc.concat({
key: item.get('id'),
style: {
height: 0,
padding: 0,
translateY: -32,
opacity: 0
},
data: item
});
}, []);
}
getStyles = () => {
return (prevStyles) => {
return this.props.items.reduce((acc, item, index) => {
const isFirstItem = (index === 0);
return acc.concat({
key: item.get('id'),
style: {
opacity: spring(isFirstItem ? 1 : prevStyles[index - 1].style.opacity),
padding: 16,
height: spring(isFirstItem ? 132 : prevStyles[index - 1].style.height, presets.gentle),
translateY: spring(isFirstItem ? 0 : prevStyles[index - 1].style.translateY, presets.gentle)
},
data: item
});
}, []);
}
}
animateEnter = () => {
return {
height: 0,
padding: 0,
translateY: -32,
opacity: 0
}
}
animateLeave = () => {
return {
height: spring(0),
padding: spring(0),
translateY: spring(-32),
opacity: spring(0)
}
}
render() {
const { onRemoveItem } = this.props;
return (
<TransitionMotion
defaultStyles={ this.getDefaultStyles() }
styles={ this.getStyles() }
willEnter={ this.animateEnter }
willLeave={ this.animateLeave }>
{ styles => (
<div>
{ styles.map(({ key, style, data: item }) => (
<div
key={ key }
style={{
...style,
transform: `translateY(${style.translateY}px)`
}}>
<CartItem
id={ item.get('id') }
album={ item.get('album') }
year={ item.get('year') }
isExplicit={ item.get('isExplicit') }
bandName={ item.get('bandName') }
genre={ item.get('genre') }
imageUrl={ item.get('imageUrl') }
priceNet={ item.getIn(['price', 'net']) }
priceGross={ item.getIn(['price', 'gross']) }
onRemove={ onRemoveItem.bind(this, item.get('id')) }
/>
</div>
)) }
</div>
) }
</TransitionMotion>
);
}
}
import { fromJS } from 'immutable';
export default fromJS({
items: [
{
id: 'abc123',
album: 'American Idiot',
isExplicit: true,
bandName: 'Green day',
year: 2004,
genre: 'Punk rock',
imageUrl: 'http://4.bp.blogspot.com/-DapqErB4heQ/UGlyQLNmEiI/AAAAAAAAACI/1szg3a2-zVc/s1600/Green_Day_-_American_Idiot_cover.jpg',
price: {
net: 19.99,
gross: 21.99
}
},
{
id: 'dec987',
album: 'Meteora',
bandName: 'Linkin park',
year: 2002,
genre: 'Alternative',
imageUrl: 'https://img.discogs.com/v71p1qmVPcG2LLDSyA0Vc-ZAzBo=/fit-in/600x600/filters:strip_icc():format(jpeg):mode_rgb():quality(90)/discogs-images/R-567665-1245380635.jpeg.jpg',
price: {
net: 13.99,
gross: 15.99
}
}
]
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment