React Native Blueprints
上QQ阅读APP看书,第一时间看更新

Adding event listeners

As it happened with the ShoppingList screen, we want the user to be able to interact with our AddProduct component, so we will add some event handlers to respond to some user actions.

Our render method should then look something like this:

/*** AddProduct.js ***/

...
render() {
return (
<Container>
<Content>
<List>
{this.state.allProducts.map(product => {
const productIsInList = this.state.productsInList.
find(p => p.id === product.id);
return (
<ListItem
key={product.id}
onPress={this._handleProductPress.bind
(this, product)}
>
<Body>
<Text
style={{ color: productIsInList? '#bbb' : '#000' }}
>
{product.name}
</Text>
{
productIsInList &&
<Text note>
{'Already in shopping list'}
</Text>
}
</Body>
<Right>
<Icon
ios="ios-remove-circle"
android="md-remove-circle"
style={{ color: 'red' }}
onPress={this._handleRemovePress.bind(this,
product )}
/>
</Right>
</ListItem>
);
})}
</List>
</Content>
<Fab
style={{ backgroundColor: '#5067FF' }}
position="bottomRight"
onPress={this._handleAddProductPress.bind(this)}
>
<Icon name="add" />
</Fab>
</Container>
);
}
...

There are three event handlers responding to the three press events in this component:

  • On the blue <Fab> button, which is in charge of adding new products to the products list
  • On each <ListItem>, which will add the product to the shopping list
  • On the delete icons inside each <ListItem> to remove this product from the list of the products, which can be added to the shopping list

Let's start adding new products to the available products list once the user presses the <Fab> button:

/*** AddProduct.js ***/

...
_handleAddProductPress() {
prompt(
'Enter product name',
'',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'OK', onPress: this.addNewProduct.bind(this) }
],
{
type: 'plain-text'
}
);
}
...

We are using here the prompt function from the react-native-prompt-android module. Despite its name, it's a cross-platform prompt-on-a-pop-up library, which we will use to add products through the addNewProduct function we created before. We need to import the prompt function before we use it, as follows:

import prompt from 'react-native-prompt-android';

And here is the output:

Once the user enters the name of the product and presses OK, the product will be added to the list so that we can move to the next event handler, adding products to the shopping list when the user taps on the product name:

/*** AddProduct.js ***/

...
_handleProductPress(product) {
const productIndex = this.state.productsInList.findIndex(
p => p.id === product.id
);
if (productIndex > -1) {
this.setState({
productsInList: this.state.productsInList.filter(
p => p.id !== product.id
)
});
this.props.navigation.state.params.deleteProduct(product);
} else {
this.setState({
productsInList: this.state.productsInList.concat(product)
});
this.props.navigation.state.params.addProduct(product);
}
}
...

This handler checks if the selected product is on the shopping list already. If it is, it will remove it by calling deleteProduct from the navigation state and also from the component's state by calling setState . Otherwise, it will add the product to the shopping list by calling addProduct in the navigation state and refresh the local state by calling setState.

Finally, we will add an event handler for the delete icon on each of the <ListItems>, so the user can remove products from the list of available products:

/*** AddProduct.js ***/

...
async _handleRemovePress(product) {
this.setState({
allProducts: this.state.allProducts.filter(p => p.id !== product.id)
});
await AsyncStorage.setItem(
'@allProducts',
JSON.stringify(
this.state.allProducts.filter(p => p.id !== product.id)
)
);
}
...

We need to remove the product from the component's local state, but also from the AsyncStorage so it doesn't show during later runs of our app.