Skip to content

Instantly share code, notes, and snippets.

@obliviusm
Created August 8, 2018 15:56
Show Gist options
  • Select an option

  • Save obliviusm/c77490c5f73024b77a310faa536641e6 to your computer and use it in GitHub Desktop.

Select an option

Save obliviusm/c77490c5f73024b77a310faa536641e6 to your computer and use it in GitHub Desktop.
React Native sample for all layers of Products Index Screen
import React, { Component } from 'react';
import { View, Image, Platform, Text } from 'react-native';
import LightText from '../Text/LightText';
import fonts from '../../assets/fonts/fonts'
import { softScreenColor } from '../../assets/styles/screens';
import colors from '../../assets/styles/colors';
import customPropTypes from '../../utils/customPropTypes';
const stylesProductItem = {
container: {
flex: 1,
margin: 10,
marginTop: 0,
backgroundColor: softScreenColor.itemBackground,
},
image: {
aspectRatio: 1.5,
},
name: {
color: softScreenColor.text,
fontSize: 16,
fontFamily: fonts.Semibold,
marginTop: Platform.OS === 'ios' ? -3 : -2,
backgroundColor: colors.transparent,
},
price: {
color: softScreenColor.text,
fontSize: 12,
marginTop: Platform.OS === 'ios' ? -3 : -2,
backgroundColor: colors.transparent,
},
};
const stylesProductScreen = {
container: {
flex: 1,
},
imageContainer: {
marginTop: 4,
marginLeft: 2,
marginRight: 2,
},
image: {
aspectRatio: 1.5,
},
textWrapper: {
marginBottom: 6,
paddingLeft: 10,
paddingRight: 20,
},
name: {
color: softScreenColor.text,
fontSize: 20,
fontFamily: fonts.DisplaySemibold,
marginTop: -2,
backgroundColor: colors.transparent,
},
price: {
color: softScreenColor.text,
fontSize: 14,
marginTop: -3,
backgroundColor: colors.transparent,
},
};
const stylesProduct = {
item: stylesProductItem,
screen: stylesProductScreen,
};
class Product extends Component {
render() {
const { pricePerDay, name } = this.props.product;
const { displayType } = this.props;
const styles = stylesProduct[displayType];
return (
<View style={styles.container}>
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={{ uri: this.props.product.photoThumbUrl }}
/>
</View>
<View style={styles.textWrapper}>
<Text numberOfLines={1} style={styles.name}>
{name}
</Text>
<LightText numberOfLines={1} style={styles.price}>
{`$${pricePerDay} per day`}
</LightText>
</View>
</View>
);
}
}
Product.propTypes = {
product: customPropTypes.product.isRequired,
displayType: customPropTypes.productDisplayType.isRequired,
};
export default Product;
import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
import { View } from 'native-base';
import { Actions } from 'react-native-router-flux';
import RegularText from '../Text/RegularText';
import Product from '../ProductShow/Product';
import FlatListWithPlaceholder from './FlatListWithPlaceholder';
import customPropTypes from '../../utils/customPropTypes';
import { softScreenColor } from '../../assets/styles/screens';
const styles = {
productsList: {
alignSelf: 'stretch',
},
productContainer: {
flex: 1
},
productPlaceholder: {
margin: 10,
flex: 1
},
emptyRefreshingWrapper: {
height: 100,
},
text: {
color: softScreenColor.text,
fontSize: 16,
paddingLeft: 15,
paddingRight: 45,
paddingTop: 15,
},
};
class ProductsList extends Component {
componentDidMount() {
this.props.requestProductsList()
}
handleRefresh() {
this.props.requestProductsList();
}
keyExtractor(item) {
return item.id;
}
renderPlaceholder() {
return <View style={styles.productPlaceholder} />;
}
renderProduct({ item }) {
return (
<TouchableOpacity
onPress={() => Actions.ProductShow({ productId: item.id })}
style={styles.productContainer}
>
<View style={styles.productContainer}>
<Product
key={() => this.keyExtractor(item)}
product={item}
displayType="item"
/>
</View>
</TouchableOpacity>
);
}
renderEmptyComponent = () => {
if (this.props.firstFetching) {
return <View style={styles.emptyRefreshingWrapper} />;
} else {
return this.props.ListEmptyComponent;
}
}
render() {
const { products, firstFetching } = this.props;
return (
<FlatListWithPlaceholder
style={styles.productsList}
data={products}
renderItem={this.renderProduct}
renderPlaceholder={this.renderPlaceholder}
keyExtractor={this.keyExtractor}
numColumns={2}
ListEmptyComponent={this.renderEmptyComponent()}
onRefresh={() => this.handleRefresh()}
refreshing={firstFetching}
/>
);
}
}
ProductsList.propTypes = {
products: PropTypes.arrayOf(customPropTypes.product),
requestProductsList: PropTypes.func.isRequired,
ListEmptyComponent: PropTypes.node.isRequired,
firstFetching: PropTypes.bool,
};
ProductsList.defaultProps = {
products: [],
firstFetching: false,
};
export default ProductsList;
import { Map, Record } from 'immutable';
import actions from './actions';
import availabilities from '../../../constants/product';
export const Product = Record({
id: -1,
isOwner: false,
name: 'Title',
category: -1,
description: '',
owner: -1,
photoThumbUrl: 'uri',
photoUrl: 'uri',
pricePerDay: 0,
availability: availabilities.ALWAYS_AVAILABLE,
});
const InitialState = Record({
byId: Map(),
allIds: [],
myAllIds: [],
});
const initialState = new InitialState();
function productsReducer(state = initialState, action) {
switch (action.type) {
case actions.PRODUCT_SAVE: {
const product = action.payload;
const nextState = state.setIn(
['byId', product.id],
new Product(product)
);
return nextState;
}
case actions.PRODUCTS_SAVE: {
const { products, sortedIds } = action.payload;
let nextState = state.setIn(['allIds'], sortedIds);
products.forEach(product => {
nextState = nextState.setIn(
['byId', product.id],
new Product(product)
);
});
return nextState;
}
case actions.ALL_LISTED_PRODUCTS_SAVE: {
const { products, sortedIds } = action.payload;
let nextState = state.setIn(['myAllIds'], sortedIds);
products.forEach(product => {
nextState = nextState.setIn(
['byId', product.id],
new Product(product)
);
});
return nextState;
}
default: {
return state;
}
}
}
export default productsReducer;
import { call, put, select } from 'redux-saga/effects';
import { getFilterFields } from '../reducers/filterData/reducer';
import { allProductsSuccess as allProductsSuccessAction } from '../reducers/products/productsActions';
import {
productsSave as productsSaveAction,
productSave as productSaveAction
} from '../reducers/entities/products/actions';
import { userSave as userSaveAction } from '../reducers/entities/users/actions';
import { authenticatedApiRequest } from './apiSagas';
import api from '../services/api';
import { normalizeProductShow, normalizeAllProducts } from '../utils/normalizer';
export function* allProductsRequest() {
const filterFields = yield select(getFilterFields);
const params = {
search_text: filterFields.searchText,
start_date: filterFields.startingDay,
end_date: filterFields.endingDay
};
const response = yield call(authenticatedApiRequest, api.productsIndex, params);
if (response.ok) {
const productsResponse = response.data.products;
const { products, sortedIds } = yield call(normalizeAllProducts, productsResponse);
yield put(productsSaveAction({ products, sortedIds }));
yield put(allProductsSuccessAction());
}
}
export function* productRequest(action) {
const { productId } = action.payload;
const params = { productId };
const response = yield call(authenticatedApiRequest, api.productShow, params);
if (response.ok) {
const productResponse = response.data;
const { product, user } = yield call(normalizeProductShow, productResponse);
yield put(productSaveAction(product));
yield put(userSaveAction(user));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment