I am trying to use React Native to create a To-do list application. And use the UseState hook to add more task to an array to map on the front-end. But I cannot use useState to add new item in the array.
This is the code in App.js
const [task, setTask] = useState('');
const [taskItems, setTaskItems] = useState([]);
const handleAddTask = () => {
Keyboard.dismiss();
setTaskItems([...taskItems, task]);
console.log(task);
console.log(taskItems);
}
I tried to change
setTaskItems([...taskItems, task]);
to
setTaskItems(taskItems => [...taskItems, task]);
That is not working.
What am I missing / doing wrong? Any help is highly appreciated!
Here is the full code with out style sheet.
import React, {useState} from 'react';
import { KeyboardAvoidingView, StyleSheet, Text, View, TextInput, TouchableOpacity, Platform, Keyboard, ScrollView } from 'react-native';
import Task from './components/Task';
export default function App() {
const [task, setTask] = useState('');
const [taskItems, setTaskItems] = useState([]);
const handleAddTask = () => {
Keyboard.dismiss();
const newTaskItems = [...taskItems, task];
setTaskItems(newTaskItems);
console.log(task);
console.log(taskItems);
setTask(' ');
}
const completeTask = (index) => {
let itemsCopy = [...taskItems];
itemsCopy.splice(index, 1);
setTaskItems(itemsCopy)
}
return (
<View style={styles.container}>
{/* Added this scroll view to enable scrolling when list gets longer than the page */}
<ScrollView
contentContainerStyle={{
flexGrow: 1
}}
keyboardShouldPersistTaps='handled'
>
{/* Today's Tasks */}
<View style={styles.tasksWrapper}>
<Text style={styles.sectionTitle}>Today's tasks</Text>
<View style={styles.items}>
{/* This is where the tasks will go! */}
{
taskItems.map((item, index) => {
return (
<TouchableOpacity key={index} onPress={() => completeTask(index)}>
<Task text={item} />
</TouchableOpacity>
)
})
}
</View>
</View>
</ScrollView>
{/* Write a task */}
{/* Uses a keyboard avoiding view which ensures the keyboard does not cover the items on screen */}
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.writeTaskWrapper}
>
<TextInput style={styles.input} placeholder={'Write a task'} value={task} onChangeText={text => setTask(text)} />
<TouchableOpacity onPress={() => handleAddTask()}>
<View style={styles.addWrapper}>
<Text style={styles.addText}>+</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</View>
);
}
2
Answers
I tested your code localy and work fine, it is a little issue with
and the rest is working properly.
Something that may you confuse is the
The setTaskItems function is asynchronous, which means that it does not update the taskItems state immediately. Instead, it schedules a state update and returns immediately. This means that when you call console.log(taskItems) immediately after calling setTaskItems(newTaskItems), the taskItems state has not yet been updated.
To see the updated taskItems state, you can use the useEffect hook to log the state after it has been update, but your change should be reflect in your UI.
Check this for more ref React useState hook is asynchronous!
The setter state function is an asynchronous function. So when you update the
task
it won’t be guaranteed to run thetaskItems
value exactly after the update of thetask
.I recommend you use
useEffect
anduseRef
to run thesetTaskItems
function exactly after the update of thetask
value.