skip to Main Content

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


  1. I tested your code localy and work fine, it is a little issue with

        setTask(' '); change to  --> setTask('');
    

    and the rest is working properly.

    Something that may you confuse is the

    console.log(taskItems);
    enter code here
    

    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.

      useEffect(() => {
    console.log(taskItems);
    }, [taskItems]);
    

    Check this for more ref React useState hook is asynchronous!

    Login or Signup to reply.
  2. The setter state function is an asynchronous function. So when you update the task it won’t be guaranteed to run the taskItems value exactly after the update of the task.

    I recommend you use useEffect and useRef to run the setTaskItems function exactly after the update of the task value.

    const didMountRef = useRef(false);
    
    useEffect(() => {
      if (didMountRef.current) {
        // Here the setTaskItems will be called exactly after the task gets updated
        setTaskItems(taskItems => [...taskItems, task]);
      } else {
        didMountRef.current = true;
      }
    }, [task]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search