State, Events & Lists

Using State and Events in react native apps.

Managing State

We manage state in a react native app in the same way we managed state in a react web app. We use the inbuilt useState hook provided by react to manage the internal state of the app.

Handling Events

We handle event in react native in the same way we use to do in our react apps. The only difference here is, the core component property names are different! For Button, the event handler for onClick is onPress, whereas for TextInput, the onChange event handler is now onChangeText.

Outputting Lists

We render lists in react native the same way we did in react web apps. Below is a full demonstration of all the above mentioned building blocks for a react native app.

App.js
import React, {useState} from 'react';
import {StyleSheet, View, Text, TextInput, Button} from 'react-native';
import {StatusBar} from 'expo-status-bar';

const App = () => {
  const [newGoal, setNewGoal] = useState('');
  const [goals, setGoals] = useState([]);
  
  const goalInputHandler = (goal) => {
    setNewGoal(goal)
  }
  
  const addGoalHandler = () => {
    setGoals((prevState) => [...prevState, newGoal])
  }
  
  return (
    <View style={styles.root}>
      <View style={styles.inputContainer}>
        <TextInput
          placeholder="Todo Goals"
          style={styles.input}
          onChangeText={goalInputHandler}
          value={newGoal}
        />
        <Button title="ADD" onPress={addGoalHandler} />
      </View>
      <View>
        {goals.map((goal) => <Text key={Math.random()}>{goal}</Text>)}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: { padding: 50},
  inputContainer: {
    flexDirextion: 'row',
    justifyContent: 'space-between',
    alignItems: 'center'
  },
  input: {
    width: '80%',
    padding: 10,
    borderColor: 'black',
    borderWidth: 1
  }
})

export default App;

Styling List Items

Since the Text component provided by react native does not support much styling options, we will wrap it within a View component to provide styling to the list item, which is a Text component here.

Also, since we will be repeating the View instead of the Text component, we will move our key prop to the View instead of Text component.

App.js
import //...

const App = () => {
  //...
  return (
    <View>
      <View>
        //...
      </View>
      <View>
        {goals.map((goal) => 
          <View key={Math.random()} style={styles.listStyle}>
            <Text>{goal}</Text>
          </View>
        )}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  //...
  listStyle: {
    padding: 10,
    marginVertical: 10,
    borderColor: 'black',
    backgroundColor: '#cc',
    borderWidth: 1
  }
})

export default App

This is what it looks like now:

ScrollView

We need to explicitly tell react native that our app extends beyond the default screen size, and we do so by declaring a ScrollView component to the component where we want the content to be scrollable.

In the above example, we would wrap our View component with a ScrollView component, so that the list of items can be scrollable if there are items that exceed the view of the device.

App.js
//...
      <ScrollView>
        {goals.map((goal) => 
          <View key={Math.random()} style={styles.listStyle}>
            <Text>{goal}</Text>
          </View>
        )}
      </ScrollView>

By just adding this component, we can scroll our content that extends beyond the screen size!

Flatlist: A better list

A ScrollView component will be inefficient with very large lists, where we typically do not know the size of the list. ScrollView renders all the components in the list, in advance, even the ones that are not visible on the device screen. So scrolling down or performing any operations on such a list with the ScrollView component, can significantly slow down our app.

React native offers a component that handles lists with large items, called FlatList. We can import this component from react-native package as well.

The FlatListcomponent has two important properties:

  1. data: This is where we point to our input data.

  2. renderItem: This accepts a function, which is called for every items in our data, to render a list item.

  3. keyExtractor : This is a function that tells the FlatList how to extract the key from the data prop. By default, the logic is to look at the item and look for a key or id property, but with the keyExtractor, we can change this. It accepts a function, that takes in two arguments:

    1. item: The item it is looking at.

    2. index: The index of the item it is looking at.

App.js
//...
  const addGoalHandler = () => {
    setCourseGoals((prevState) => [
      ...prevState,
      { key: Math.random().toString(), value: newGoal },
    ]);
  }
  //...
      <FlatList
        data={courseGoals}
        renderItem={(itemData) => (
          <View style={styles.listStyle}>
            <Text>{itemData.item.value}</Text>
          </View>
        )}
      />

The FlatList component automatically adds the key property to the list item, but only if the data prop in the component follows a certain structure. The data we provide to the FlatList must be an object with the key property. We can then have any other properties within it, so ideally only the key property is required for a FlatList to automatically add keys to the list items.

React Native now supports both key and id as a property to the data props in a FlatList component. If we try accessing something like uid, it will show us a warning!

To use a custom key or id prop with the FlatList, we can make use of the keyExtractor prop:

App.js
//...
  const addGoalHandler = () => {
    setCourseGoals((prevState) => [
      ...prevState,
      { uid: Math.random().toString(), value: newGoal },
    ]);
  }
  //...
      <FlatList
        keyExtractor={(item, index) => item.uid}
        data={courseGoals}
        renderItem={(itemData) => (
          <View style={styles.listStyle}>
            <Text>{itemData.item.value}</Text>
          </View>
        )}
      />

Last updated