Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Avoid Fetching Data Twice When Using React Hooks

I'm trying to fetch a set of questions from a Firestore database, and then store them in a local array for use later on in a quiz. As I'm working to fetch the data, it's running asynchronously. I've dealt with this before, and the solution was a useEffect() hook and conditionally rendering component.

My code currently looks like this:

var questions = [];
var collection = useRef('Planning');
var [loading, setLoading] = useState(true);  

useEffect(() => {
        if (props.track === 'Testing & Deployment') {
            collection.current = 'Test_Deploy';
        } else {
            collection.current = props.track;
        }

        if(questions.length !== 0){
            setLoading(false)
        } else {
            var questionSet = db.collection('Quizzes').doc(collection.current).collection('Questions')

            questionSet.withConverter(questionConverter).get().then(function(response) {
                    response.forEach(document => {
                        var question = document.data();
                        // running asynch - need to address
                        console.log('Question in')
                        questions.push(question)
                    })
            })
            setLoading(false)
        }

    }, [props.track, questions])}

Depending on where setLoading() is, the component either won't re-render at all, or will double fetch the data from Firestore. I've tried playing around with useCallback() to no avail, and have also seen that this might be an issue with React StrictMode.

Any advice on how to fix my code in order to re-render the page after fetching all of the questions, without double fetching them?

like image 924
Broski-AC Avatar asked Oct 29 '25 03:10

Broski-AC


1 Answers

To fetch data once on component mount you can specify an empty dependency array. I also suggest moving the questions object into local component state. Since the state is updated in a loop you should use a functional state update to add each new question to the array.

const [questions, setQuestions] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
  const questionSet = db
    .collection("Quizzes")
    .doc(collection.current)
    .collection("Questions");

  questionSet
    .withConverter(questionConverter)
    .get()
    .then(function (response) {
      response.forEach((document) => {
        const question = document.data();
        setQuestions(questions => [...questions, question]);
      });
    });

  setLoading(false);
}, []);

If the linter complains about missing dependencies then you need to either add them and place the appropriate conditional check on the data fetch, or add a comment to ignore/disable the linter for the line with the dependency array, // eslint-disable-next-line react-hooks/exhaustive-deps.

like image 101
Drew Reese Avatar answered Oct 31 '25 17:10

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!