0

I have a question about useEffect. My useEffect is not fetching the data the first time, I have to switch route for it to have the data I needed

const Comments = ({ ...rest }) => {
  const theme = useTheme();
  const classes = useStyles({ theme });
  const [users, setUsers] = useState([]);
  const { push } = useHistory();
  const { token, loading } = useContext(AuthContext)

  const dispatch = useDispatch();
  const allUsers = useSelector(state => state.allUsers);
  const comments = useSelector(state => state.listCommentsByBookId);
  const listBooks = useSelector((state) => state.userListBooks);
  const isFetching = useSelector((state) => state.isFetching);
  const [stateReady, setReadyForRender] = useState(false)

  const redirectTo = ( rowData ) => {
    push({
      pathname: ROUTE.USERS_DETAILS,
      user: rowData
    });
  }

  const options = {
    filterType: 'checkbox',
    selectableRowsHeader: false,
    selectableRowsHideCheckboxes: false,
    selectableRowsOnClick: false,
    onRowClick: redirectTo,
  };

  const getAllComments = async () => {
    var allusersId = [];

    //get all ids
    await allUsers.map((user) => {
      allusersId.push(user.uid);
    })

    //get all books from users 
    await allusersId.map(async (id) => {
      await dispatch(getUserListBooks(apiURL + `api/bdd/userListBooks/${id}`, token))
    })

    var listArray = [];


    //filter the array and delete empty rows
    listArray.push(listBooks);
    var newArray = listArray.filter(e => e);
    
    //map every user and stock the list of books in string 
    await newArray.forEach(async (book)=> {
      await book.map(async (book) => {
        await dispatch(getCommentsByBookId(apiURL + `api/bdd/ratingByBook/${book.id}`, token));
      })
    })
    setReadyForRender(true)  
  }
  
  useEffect(() => {
    console.log('is fetching', isFetching)
    if(comments.length === 0) {
      getAllComments();
    }
  }, [stateReady])

  console.log('COM', comments);

  return (
    <div>
     {stateReady && 
      <Card>
        <Box className={classes.tableContainer} sx={{ minWidth: 1050 }}>
          <MUIDataTable
            data={comments}
            columns={columns}
            options={options}
          />
        </Box>
      </Card>}
    </div>
  );
};

Why? It might be related to async await but I'm stuck here.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Jats1596
  • 1,117
  • 1
  • 13
  • 20

4 Answers4

0

If you want to fetch these informations on the first render, you'll have to pass an empty array as the second parameter of your useEffect.

The reason your useEffect is not called is because stateReady does not change during the course of your current code.

See this link, particularly the note section, it explains way better than me how the empty array as second parameter works.

Gaëtan Boyals
  • 1,193
  • 1
  • 7
  • 22
  • Hi thanks for the answer. My bad about the stateReady, i've been trying to make it work using a state which will be passed to true in the fetch function but even without it it's not working.. – Jats1596 Jun 22 '21 at 12:19
  • Are you still passing an empty array `[]` as second parameter of `useEffect`? Is the `console.log()` in your useEffect is showing? – Gaëtan Boyals Jun 22 '21 at 12:22
0

You should remove stateReady from your dependency array in the useEffect hook. Adding variables in the dependency array means that the use Effect hooks fires only when one of the dependencies changes. Here's how to use useEffect as lifecycle methods https://reactjs.org/docs/hooks-effect.html

useEffect(() => {
  console.log('is fetching', isFetching)
  if(comments.length === 0) {
    getAllComments();
  }
});
Nimantha
  • 6,405
  • 6
  • 28
  • 69
SakisTsalk
  • 796
  • 7
  • 18
  • Hi thanks for the answer. My bad about the stateReady, i've been trying to make it work using a state which will be passed to `true` in the fetch function but even without it it's not working.. – Jats1596 Jun 22 '21 at 12:18
  • Hmm, I also see that you're using an async inside your `forEach`, which will not work. You should switch it in a normal for loop. Like this [thread](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop). – SakisTsalk Jun 22 '21 at 12:22
  • Tried with a normal for loop but nothing changed – Jats1596 Jun 22 '21 at 14:15
  • Okay, are you sure that all your async operations are being triggered correctly? I mean if you fixed the useEffect hook your react code looks fine. – SakisTsalk Jun 22 '21 at 15:24
0

Can you replace the useEffect section to the below code:

useEffect(() => {
    (async () => {
     console.log('is fetching', isFetching)
    if(comments.length === 0) {
      getAllComments();
    }
    })()
   }, [stateReady])

You can read more about this in this link

Varun
  • 597
  • 7
  • 26
0

You can use eslint to show errors when coding with hooks. In this case if you want useEffect to handle stateReady, please provide it in the function getAllComments() => getAllComments(stateReady) and when you call this function in useEffect with [stateReady] as dependencies, it'll work.

Moritz Mahringer
  • 1,240
  • 16
  • 28