I want rerender when props data change with react hooks but i got infinite rerender

Viewed 118

Sorry for my bad English.. But I need help you guys! The problem is I wanna rerender when props change which is in EditField Components.

So I tried two ways,

first, I was trying to using useEffect parameter In UseEffect Hooks I tried useEffect(() => {},[facils]... like this way but when I tried this way, I got infinite rerender...

So I tried another ways, I passed GetDB function to EditField Components and when i edit my props data i tried call GetDBfunction.

But it didn't work what I expected...

could you give me any advices..?

const GetDB = () => {
  const [facils, setFacils] = useState([]);

  useEffect(() => {
    firebase
      .firestore()
      .collection("facils")
      .get()
      .then((snapshot) => {
        const newFacils = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        // prevent firebase quote exceed
        console.log("!!!!FB warniing!!!!");
        setFacils(newFacils);
      });
  }, []);
  return facils;
};

const FacilityLists = () => {
  const facils = GetDB();
  const [id, setId] = useState([]);
  const [name, setName] = useState([]);
  const [showEdit, setShowEdit] = useState(false);

...
...
return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-evenly" }}>
        <div>Id</div>
        <div>시설이름</div>
      </div>
      <div>
        {facils.length > 0 ? (
          facils.map((item) => (
            <ul key={item.id} onClick={() => EditItem(item.id, item.name)}>
              {item.id} - {item.name}
            </ul>
          ))
        ) : (
          <p>there is no data</p>
        )}
      </div>
      <div>
        {showEdit ? (
          <EditField
            id={id}
            name={name}
            showSave={() => setShowEdit(false)}
            handleChange={handleChange}
            refreshData={() => GetDB}
          />
        ) : (
          <CreateField />
        )}
      </div>
    </div>
3 Answers

You cannot call a hook from a handler function. What you can do is implement and expose a fetcher from GetDB hook and call it.

Also make sure that when you create custom hooks, you prefix their name with use since it will tell react to apply the rules of hooks on it and warn you for incorrect usage

const useGetDB = () => {
  const [facils, setFacils] = useState([]);
  const fetchData = () => {
      firebase
      .firestore()
      .collection("facils")
      .get()
      .then((snapshot) => {
        const newFacils = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        // prevent firebase quote exceed
        console.log("!!!!FB warniing!!!!");
        setFacils(newFacils);
      });
  }
  useEffect(() => {
    fetchData();
  }, []);
  return { facils, fetchData};
};

Post that you can use it like below

const FacilityLists = () => {
  const {facils, fetchData} = useGetDB();
  const [id, setId] = useState([]);
  const [name, setName] = useState([]);
  const [showEdit, setShowEdit] = useState(false);

...
...
return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-evenly" }}>
        <div>Id</div>
        <div>시설이름</div>
      </div>
      <div>
        {facils.length > 0 ? (
          facils.map((item) => (
            <ul key={item.id} onClick={() => EditItem(item.id, item.name)}>
              {item.id} - {item.name}
            </ul>
          ))
        ) : (
          <p>there is no data</p>
        )}
      </div>
      <div>
        {showEdit ? (
          <EditField
            id={id}
            name={name}
            showSave={() => setShowEdit(false)}
            handleChange={handleChange}
            refreshData={() => fetchData()}
          />
        ) : (
          <CreateField />
        )}
      </div>
    </div>

Rename your GetDB to useFacils. A hook's name starts with use word, and hooks (useEffect) are only called within function components or other hooks. Not sure why you didn't got a lint error when you created the GetDb hook.

from the docs:

Its name should always start with use so that you can tell at a glance that the rules of Hooks apply to it.

and also:

Do I have to name my custom Hooks starting with “use”? Please do. This convention is very important. Without it, we wouldn’t be able to automatically check for violations of rules of Hooks because we couldn’t tell if a certain function contains calls to Hooks inside of it.

This was just an advice regarding your hooks name, as Shubham pointed in his answer, to make the refresh work, you should also make your custom hook return the fetch/refresh function.

const useDbFacils = () => {
  const [facils, setFacils] = useState([]);

  const fetchFacils = React.useCallback(() => {
    firebase
      .firestore()
      .collection("facils")
      .get()
      .then((snapshot) => {
        const newFacils = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        setFacils(newFacils);
      });
  }, [setFacils]);
  useEffect(() => {
    fetchFacils();
  }, [fetchFacils]); // only you knows if this dependency is needed
  return [facils, fetchFacils];
};

In your component:

function FacilsList() {
  const [facils, fetchFacils] = useDbFacils();

  // later
  {facils.length > 0 ? facils.map...
  // and
  <EditField
    id={id}
    name={name}
    showSave={() => setShowEdit(false)}
    handleChange={handleChange}
    refreshData={fetchFacils}
  />
}

You need to preset the custom hook with "use", or react will not recognize it as a hook and will create a new hook on every render because it will not be memorized.

Change it to useDB and it should work.

Related