Creating an updating FlatList in React Native with TextInput

Add items dynamically to a FlatList using TextInput and a Button

I took far too long to figure out how to do this.

Say you want to make a list in React Native that updates upon you pressing a button. This is very easy to do in Vanilla JavaScript and HTML, but since in React and React Native there is no DOM object, it can be a little more obscure to figure out.

The example I am going to use here is a simple "ToDo" list, but there are more applications than this. The ToDo just makes it easier to see the basics of what's happening.

State

You will need to use React State to get your list to update. First, import the necessary components. I will need useState, StyleSheet, Text, View, Button, TextInput, and FlatList:

import React, {useState} from 'react';

import {
  StyleSheet,
  Text,
  View,
  Button,
  TextInput,
  FlatList,
} from 'react-native';

Next, create your JSX element. This ToDo list will be a stand-alone React Component you can import.

function Tasks(): JSX.Element {
return()
}

You will need two state items: the list, and the String items in the list:

    const[tasks, setTasks] = useState("");
    const[taskList, setTaskList] = useState([]);

Next, write the UI itself. This makes it easier to "see" what you require. I need a wrapping View, then Text, a TextInput, and the FlatList:

return(
        <View>
            <Text style>What tasks would you like to add?</Text>
                <TextInput
                    onChangeText={setTasks}
                    value={tasks}
                    placeholder="ToDo" />
                <Button onPress={}
                    title="Submit"
                    color="#08b"
                    accessibilityLabel="Submit the task you entered"/>
            <FlatList
                data={taskList}
                renderItem={} />
        </View>
    )

Quick styling, including spacers and lines:

    return(
        <View style={taskingStyles.main}>

            <View style={taskingStyles.verticalSpacer}></View>
            <Text style={taskingStyles.text}>What tasks would you like to add?</Text>
            <View style={taskingStyles.verticalSpacer}></View>
            <View style={taskingStyles.row}>
                <TextInput
                    style={taskingStyles.minutes}
                    onChangeText={setTasks}
                    value={tasks}
                    placeholder="ToDo" />
                <View style={taskingStyles.horizontalSpacer}></View>
                <Button onPress={}
                    title="Submit"
                    color="#08b"
                    accessibilityLabel="Submit the task you entered"/>

            </View>
            <View style={taskingStyles.verticalSpacer}></View>
            <FlatList
                style={taskingStyles.task}
                data={taskList}
                renderItem={}
            />


            <View style={taskingStyles.verticalSpacer}></View>

            <View style={taskingStyles.line}></View>
            <View style={taskingStyles.verticalSpacer}></View>
        </View>
    )
}

const taskingStyles = StyleSheet.create({
    main: {
    alignItems: 'center',
    justifyContent: 'space-between',
    },

    item: {
        backgroundColor: '#fff',
    },

    verticalSpacer: {
    height: 10,
    },

    text: {
        fontSize: 10,
        color: '#000',
    },

    row: {
        flexDirection: 'row',
        alignItems: 'center',

    },

    line: {
       borderWidth: 1,
       borderColor: '#666',
       width: '90%',
       },


    minutes: {
        height: 34,
        width: '30%',
        borderWidth: 1,
        borderColor: '#08b',
        fontSize: 10,
        backgroundColor: '#fff',
    },

    task: {
            width: '100%',
            borderWidth: 1,
            borderColor: '#08b',
            backgroundColor: '#fff',
    }

});

Then we need a function that will update the list using State. It is nearly always better to use a const than a var with arrays; I was having trouble with using const and concat(), so ended up making it a var. That will be my next line of experimentation, though. Add it inside your JSX component:

const addTasks = () => {
        var arr = [...taskList];
        arr.push(tasks);
        setTaskList(arr);

    }

Mistake - putting your text directly inside renderItem

We also need an object for renderItem in the FlatList. This the main place I tripped up initially. DO NOT put your tasks directly inside the object in the Text component! All that will happen is the list will only show the String that is currently entetred into the TextInput at any one time. You need to use a prop that is passed to the object, here I am just calling it "item":

const renderTaskItem = ({item}) => {
        return(
            <View style={taskingStyles.item}>
                <Text>{item}</Text>
            </View>)
     }

//And the FlatList below:

         <FlatList
              style={taskingStyles.task}
              data={taskList}
              renderItem={renderTaskItem}
          />

Finally, you need to link the Button's onPress event to the addTasks function. Don't call it directly from the button or it will be called on every render. Use arrow function syntax instead:

 <Button onPress={() => {addTasks(tasks)}}
         title="Submit"
         color="#08b"
         accessibilityLabel="Submit the task you entered"/>

Final result

Here is the result:

And the final code all together (don't forget to export your component as well). I hope this will be helpful for someone. Going from Vanilla JS to a framework can be a little confusing, especially something like React Native where you are working within the rules of mobile applications, not web pages.

import React, {useState} from 'react';

import {
  StyleSheet,
  Text,
  View,
  Button,
  TextInput,
  FlatList,
} from 'react-native';


function Tasks(): JSX.Element {
    const[tasks, setTasks] = useState("");
    const[taskList, setTaskList] = useState([]);

    const addTasks = () => {
        var arr = [...taskList];
        arr.push(tasks);
        setTaskList(arr);

    }


    const renderTaskItem = ({item}) => {
        return(
            <View style={taskingStyles.item}>
                <Text>{item}</Text>
            </View>)
     }


    return(
        <View style={taskingStyles.main}>
            <View style={taskingStyles.verticalSpacer}></View>
            <Text style={taskingStyles.text}>What tasks would you like to add?</Text>
            <View style={taskingStyles.verticalSpacer}></View>
            <View style={taskingStyles.row}>
                <TextInput
                    style={taskingStyles.minutes}
                    onChangeText={setTasks}
                    value={tasks}
                    placeholder="ToDo" />
                <View style={taskingStyles.horizontalSpacer}></View>
                <Button onPress={() => {addTasks(tasks)} }
                    title="Submit"
                    color="#08b"
                    accessibilityLabel="Submit the task you entered"/>

            </View>
            <View style={taskingStyles.verticalSpacer}></View>
            <FlatList
                style={taskingStyles.task}
                data={taskList}
                renderItem={renderTaskItem}
            />


            <View style={taskingStyles.verticalSpacer}></View>
            <View style={taskingStyles.line}></View>
            <View style={taskingStyles.verticalSpacer}></View>
        </View>
    )
}

const taskingStyles = StyleSheet.create({
    main: {
    alignItems: 'center',
    justifyContent: 'space-between',
    },

    item: {
        backgroundColor: '#fff',
    },

    verticalSpacer: {
    height: 10,
    },

    text: {
        fontSize: 10,
        color: '#000',
    },

    row: {
        flexDirection: 'row',
        alignItems: 'center',

    },

    line: {
       borderWidth: 1,
       borderColor: '#666',
       width: '90%',
       },


    minutes: {
        height: 34,
        width: '30%',
        borderWidth: 1,
        borderColor: '#08b',
        fontSize: 10,
        backgroundColor: '#fff',
    },

    task: {
            width: '100%',
            borderWidth: 1,
            borderColor: '#08b',
            backgroundColor: '#fff',
    }

});

export default Tasks;