0

Assume having a react application related to a school system with this scenario: having many classes and each class has many classroom and the user can filter the class and the classroom At the first render the page will show the student in the first class and the first classroom

then whenever the user changes the class the page must change the classrooms and show the students in the first classroom related to the selected class

and if the user changes the classroom the page must load the students in the selected classroom

two problems I'm facing are:

  • Is it okay to change many states (the selected class and the associating classrooms) at the same use effect.
  • Having many use effect, the first for the initial rendering and the second for class changing and the third for classroom changing, and when the component mount it call the three hooks together

What's the best approach for such scenario ?

  • Is this answer to your question ? https://stackoverflow.com/questions/54002792/in-general-is-it-better-to-use-one-or-many-useeffect-hooks-in-a-single-component – Evren Jan 30 '22 at 22:54
  • @Evren I saw the post, unfortunately it's not the answer, my situation is not about splitting the logic into many use effects but it's having every effect executed at the first time render and multiple state changing, so I didn't know the best approach for that – Abd al rahman shebani Jan 30 '22 at 23:04
  • Unless you need to react to different states changing independently, you can include them all in the dependency array of one effect hook call. It's ok to update multiple React states within one effect hook callback. – jsejcksn Jan 30 '22 at 23:34
  • @jsejcksn if I put them inside one hook is it okay to call multiple function to update the state in one use effect ? like `setClass()` then `setStudents()` ? and in the situation of having multiple use effects each one handles an API request(to get classes, to get students ..) in the first render all of them will be called but I want them to wait for classes and classrooms to be fetched first, Is the best solution to use a condition before calling an APIto make sure that the previous data is fetched first ? – Abd al rahman shebani Jan 31 '22 at 11:59
  • 1
    @Abdalrahmanshebani Rather than continue to discuss about generalized concepts, I'll post an answer with some concrete guidance. – jsejcksn Jan 31 '22 at 18:24

1 Answers1

1

Here's a rough idea of how you could do it, based on the description of your data structures. You didn't share much about the structures, so I used string IDs (but maybe you have them in arrays and you could use integer indexes instead).

The important part is the demonstration of how one effect hook can update a state value that the other one depends on, and the next one can react and update more state.

Then, you conditionally render different JSX depending on your state.

If you're unfamiliar with TypeScript and TS syntax, you can follow the playground link below, and you'll see the plain compiled JavaScript on the right

TS Playground

import {default as React, useEffect, useState} from 'react';

// Some arbitrary types for this example
type Student = {
  id: string;
};

type Classroom = {
  id: string;
  students: { [id: string]: Student };
};

type Class = {
  id: string;
  classRooms: { [id: string]: Classroom };
};


// Functions which get the data from your API. Imagine these are in another file and you import them:
// imoprt {fetchClass, fetchClassroom} from './api';
declare function fetchClass (id: string): Promise<Class>;
declare function fetchClassroom (id: string): Promise<Classroom>;

// Determine if a value exists (is not null or undefined)
function exists <T>(maybe: T): maybe is NonNullable<T> {
  return maybe != null;
}

// The component
function Example () {
  const [classId, setClassId] = useState<string>();
  const [currentClass, setCurrentClass] = useState<Class>();
  const [classroomId, setClassroomId] = useState<string>();
  const [currentClassroom, setCurrentClassroom] = useState<Classroom>();

  useEffect(() => {
    if (!exists(classId)) return;
    (async () => {
      setCurrentClass(await fetchClass(classId));
      setClassroomId('id_of_first_classroom');
    })();
  }, [classId]);

  useEffect(() => {
    if (!exists(classroomId)) return;
    (async () => {
      setCurrentClassroom(await fetchClassroom(classroomId));
    })();
  }, [classroomId]);

  if (exists(currentClass)) {
    if (exists(currentClassroom)) {
      // return JSX created with the currentClass and currentClassroom data
    }
    else {
      // return JSX created with just the currentClass data (e.g. choose a classroom based on a list of classroom IDs)
    }
  }

  // return some JSX to get started (e.g. choose a class based on a list of class IDs)
}

jsejcksn
  • 27,667
  • 4
  • 38
  • 62