Photo by Glenn Carstens-Peters on Unsplash
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;