0

I am struggling to find why my component is not responding to being called by its parent. I am trying to integrate Cloud Firestore with code that previously ran using Redux. My first goal is to populate my List with data from Firestore.

Here are my (simplified) components in question:

// List.js
import React, { useEffect, useState } from "react";
import db from "../../db";
import { onSnapshot, query, collection, orderBy } from "firebase/firestore";

import TaskItem from "./TaskItem";

const List = () => {
  const [taskList, setTaskList] = useState([]); // Currently assumes DB never empty, populates on initial render
  const [isInitialRender, setIsInitialRender] = useState(true);

  // Firestore
  const ref = collection(db, "Tasks");
  const q = query(ref, orderBy("listIndex"));

  useEffect(() => {
      // Execute only on initial render
      if (isInitialRender) {
        // Populate task list
        onSnapshot(q, (querySnapshot) => {
            setTaskList(() => querySnapshot.docs)
        }, (error) => {
          console.log(error)
        })
      };

      setIsInitialRender(() => false);
  }, []);

  return (
    <>
      <h2>List</h2>
        {taskList.forEach((task) => ( // console-logging `task` here will output correct data
          <ul key={task.data().key}>
            <TaskItem
              id={task.data().key}
              // docRef={taskDoc}
            />
          </ul>
          ))
        }
    </>
  );
};

export default List;
// TaskItem.js
import React from "react";

const TaskItem = (props) => {
  console.log('This will not print')

  const submitHandler = () => console.log('Submitted');

  return (
    <form onSubmit={submitHandler}>
      <input
        autoFocus
        type="text"
      />
    </form>
  );
};

export default TaskItem;

I have tried:

  1. Populating the state with the data from each document (rather than assigning it directly), then passing the contents as props. This led to (I believe) an infinite loop, and ideally I would like to pass the actual DocumentReference to the TaskItem anyways. So this was a bust for me.
  2. Returning [...querySnapshot.docs], or even (prev) => prev = [...querySnapshot.docs] in the state setter. No response from TaskItem().
  3. Decomposing the taskList state into a new dummy array, and using that array to populate the props for TaskItem.

I know that the task data is being fetched successfully because I can satisfactorily log taskList's contents from the map function in List's return statement. But it seems like TaskItem() never runs.

Does anyone see my error here?

spheroidic
  • 199
  • 7

1 Answers1

1

edit: sorry I assumed you were using map. I'm not sure why your forEach isn't working but map would work, from my example

edit 2: you probably are looking to use map because you want to transform every element in the array: JavaScript: Difference between .forEach() and .map()

you forgot to return something from the map, and maybe need {} instead.

try

 {taskList.forEach((task) => {
       return ( 
          <ul key={task.data().key}>
            <TaskItem
              id={task.data().key}
              // docRef={taskDoc}
            />
          </ul>
         )
          })
Sujio
  • 357
  • 1
  • 2
  • 13
  • Thank you! You're right, the issue was `forEach`. According to your link, one difference between the two is that `map` returns the result of each callback execution as a new array, whereas `forEach` returns nothing. Great to know! – spheroidic Oct 04 '22 at 14:21