Home > OS >  UseEffect dependency array keeps running even state does not change
UseEffect dependency array keeps running even state does not change

Time:05-09

I have a useEffect which keeps running in a infinite loop even though my state, which i'm using in my dependecy array, are not changing(or am i missing something about my tasks-state that it is changing somewhere?) The useEffect is used to query and retrieve data from Firestore, and here is the code:

import { StyleSheet, View, FlatList, Animated } from 'react-native'
import React, {useEffect, useState} from 'react'
import { Subheading, Divider, Text, Modal, Button, Portal, TextInput} from 'react-native-paper';
import Swipeable from 'react-native-gesture-handler/Swipeable'
import { TouchableOpacity } from 'react-native-gesture-handler';
import { collection, where, query, getDocs, addDoc, deleteDoc} from 'firebase/firestore';
import { db} from '../../firebase/firebase'
import firebase from 'firebase/compat/app';
import uuid from "react-native-uuid";


export default function TaskComponent({route}) {
  const item = route.params.item;
  const containerStyle = {backgroundColor: 'white', padding: 60, margin: 10};
  const [tasks, setTasks] = useState({});
  const [textInput, setTextInput] = useState({name: "", description: ""});
  let userID = `${firebase.auth().currentUser.uid};`
  const filteredTasks = [];


  useEffect(() => {
    const getFilterTasks = async() => {
    const q = query(collection(db, 'allTasks'), where('userID', '==', userID), where('categoryID', '==', item.id))
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach((doc) => {
      filteredTasks.push(doc.data())
    })
    setTasks(filteredTasks)
  }

  getFilterTasks();

  }, [tasks])


  const handleChange = (name, value) => {
    setTextInput({
      ...textInput,
      [name]: value,
    });
  };


  const showModal = () => {
    setVisible(true);
  }


  const hideModal = () => {
    setVisible(false);
  }


  const addTask = (textInput) => {
    setTasks((prevState) => {
      return [
        {userID: userID, categoryID: item.id, name: textInput.name, description: textInput.description, id: uuid.v1()},
        ...prevState
      ];
    })

    addToFirebase();

    hideModal();
  }


  const deleteItem = (item) => {
    setTasks((prevState) => {
      return prevState.filter(task => task.id != item.id)
    })
  }

  const addToFirebase = async() => {
      await addDoc(collection(db, 'allTasks'), {
        userID: userID, categoryID: item.id, name: textInput.name, description: textInput.description, id: uuid.v1()
      });
  }


  const DataComponent = (item) => {
    const rightSwipe = (progress, dragX) => {
      const scale = dragX.interpolate({
        inputRange: [-100, 0],
        outputRange: [1, 0],
        extrapolate: 'clamp'
      });

      return(
        <TouchableOpacity activeOpacity={0.8} onPress={() => deleteItem(item)}>
          <View>
            <Animated.Text>Delete</Animated.Text>
          </View>
        </TouchableOpacity>
      )
    }

    return (
      <TouchableOpacity>
      <Swipeable renderRightActions={rightSwipe}>
        <View>
        <View>
          <Text>Name:</Text>
          <Text> {item.name}</Text>
        </View>
        <View>
          <Text>Date:</Text>
          <Text> {item.date}</Text>
        </View>
          <Text>Description:</Text>
          <Text>{item.description}</Text>
        </View>
      </Swipeable>
      </TouchableOpacity>
    )
  }

  return (
    <View>
      <Subheading>Your {item.name} tasks:</Subheading>
      <View>
          <FlatList
          keyExtractor={(item) => item.id}
          data={tasks}
          renderItem={ ({item}) => (
            <DataComponent {...item}/>
            )}
            />
        </View>

        <View>
        <Button mode="contained" uppercase={false} onPress={showModal}>
          Add a task
        </Button>
      </View>

      <Portal>
        <Modal visible={visible} onDismiss={hideModal} contentContainerStyle={containerStyle}>
          <Text>Name your task: </Text>
          <TextInput placeholder="Enter task name" value={textInput.name} onChangeText={(text) => handleChange('name', text)} name="name"/>
          
          <Text>Enter description:</Text>
          <TextInput multiline placeholder="Enter description" value={textInput.description} onChangeText={(text) => handleChange('description', text)}  name="description"/>

          <Button mode="contained" uppercase={false} onPress={() => addTask(textInput)}>
            Add
          </Button>
        </Modal>
      </Portal>
    </View>
  )
}

I have also tried just with an empty dependency array, but then i have to refresh the code everytime i want to see the right data.

CodePudding user response:

Just remove tasks from the triggers array.

Leave an empty array, so the useEffect will be called only at mount.


  useEffect(() => {
    const getFilterTasks = async() => {
    const q = query(collection(db, 'allTasks'), where('userID', '==', userID), where('categoryID', '==', item.id))
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach((doc) => {
      filteredTasks.push(doc.data())
    })
    setTasks(filteredTasks)
  }

  getFilterTasks();

  }, [])

CodePudding user response:

useEffect(() => {
    const getFilterTasks = async() => {
    const q = query(collection(db, 'allTasks'), where('userID', '==', userID), where('categoryID', '==', item.id))
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach((doc) => {
      filteredTasks.push(doc.data())
    })
    setTasks(filteredTasks)
  }

  getFilterTasks();

  }, [tasks])

Every time when you setTasks(filteredTasks) your useEffect is called again and setTasks is called again too. You have to add some condition into your useEffect to avoid this problem. In you case, can be something like that:

useEffect(() => {
    if (tasks.length) return;

    const getFilterTasks = async() => {
    const q = query(collection(db, 'allTasks'), where('userID', '==', userID), where('categoryID', '==', item.id))
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach((doc) => {
      filteredTasks.push(doc.data())
    })
    setTasks(filteredTasks)
  }

  getFilterTasks();

  }, [tasks])

But here you have to also change the default value of your tasks useState. From const [tasks, setTasks] = useState({}); to const [tasks, setTasks] = useState([]);;

It is to tasks.length would work.

  • Related