So how can we work around that problem?
This is such a common problem and such a common scenario
that React has a solution for it.
There is another hook which you can import from React
and that's the useEffect hook.
UseEffect is a hook that allows you to run some code
under certain conditions.
At the moment this code for sending the request
runs always when the component function runs.
With useEffect we'll be able to restrict this
and define conditions when this code should run.
So that it does not always run.
For this we call useEffect
we execute this useEffect function
directly in our component function
and then useEffect once two arguments.
The first argument is a function.
And here I will pass in an anonymous arrow function.
The second argument is an array
and array of dependencies.
And I'll come back to this array in a second.
Now, inside of this first argument inside of this function
we can execute our batch code here.
So cut this and add this into this effect function.
Now this effect function is first argument here
which we passed to useEffect
will be executed by React on our behalf.
But only under certain circumstances
not always when this component re-runs.
I'll come back to those circumstances in a second.
Now with that, we make sure that our fetch request here
our fetch function is only executed
when React executes this function.
Now when does React execute this function though?
If we would not specify this second argument this array.
If we would omit this
then this effect function would execute
whenever this component function executes.
So then we don't gain anything.
That's why you need this second argument.
You technically don't need it.
It's not required, but if you omit it
there is no difference compared to just running the code
in the component function.
With this second argument added though
React will check
the values
you add, to this array
and compare them to their equivalents
when this effect function was executed the last time.
Now, if that's an empty array, there are no dependencies.
And then React will only execute this function
when this component function rendered and executed
for the first time.
And for subsequent executions of this component function
this effect function will not run
because we have no dependencies
so the values of the dependencies are always the same
because there are no values.
Now, if you would add a dependency here, like let's say
the Isloading state snapshot
which you should not.
But if we would have this as a dependency here
then this function would execute whenever the value of
Isloading changed.
So if that value does not change
if that always stays the same
then this would also not execute again.
But if Isloading was false
and then you somewhere update it's state to true,
then the value of Isloading would have changed
and then this effect function would execute again.
Now, in reality, you don't need to think
which values should trigger your effect function
instead there is a simple rule.
In your dependencies array
you should add all external values
your effect function relies on.
So in this case, there are no external values.
The fetch function is a built in browser function
it's not one of our component props or state.
And other than that we don't use any state or prop values
in this effect function that would belong to our component.
If we would extract some data from props here though
if we would, for example, get the URL
from props.url let's say
then props would be a dependency
and we should add props here.
But that's not the case here
but that is how it would (indistinct) then.
Set Isoading and set loaded meetups
actually technically would be external dependencies
because these are constants defined outside
of this effect function.
But these state updating functions are an exception.
You can add them here and that would not be incorrect
that these functions are external dependencies.
And whenever these functions change
this effect should run again
because then these functions might do different things
than they did the last time they were executed
but React actually guarantees that these
state updating functions will never change.
They will always do exactly the same thing.
And therefore it's allowed to omit these functions here.
And hence in our scenario here, we use useEffect like this.
With an empty dependencies array
since we have no external dependencies here
and therefore this code will only run once
when this component is rendered for the first time.
Now I dive way deeper into useEffect with more examples
and also more features of this hook in my course.
But what you learn here, is the core foundation of useEffect
which you have to understand
that you can use it for running such side effects
as they are called.
Code which does not directly influence
what's showing up on the screen.
And that for such side effects
useEffect is the proper solution
for controlling when this code should run.
Now with that, we actually should also set
Isloading to true here
at the beginning of this effect function
so that whenever this affect would run again
we set this to true again.
It does run again here, but still that is cleaner.
I would argue.
And then we sent this request and ultimately
we update our loading state again
and we set our loaded meetups to the data we fetched here.
And now with that, its still won't work
because setting the data like this is incorrect
as it turns out
but we don't have an infinite loop.
And I hope I could make it clear why that's the case.
If we now save everything, our app crashes
because as I said, we're setting the wrong data
but we don't have an infinite loop.
So now what's wrong with that data though.
The problem with our data is that
when we fetch meetups from Firebase,
we actually don't get an array.
Instead, we get an object where these
cryptic auto-generated IDs
act as properties.
So when we send the get request to our Firebase API
we get back an object with two properties in this case
and these here are our property names.
And then nest it inside of these properties
we got nested objects with the actual meetup data.
Now here in our component though
we expect an array.
Because in meetup list we map on our data
and that only exists on an array, not on our object.
Hence we need to transform the data here in all meetups
before we set our state.
Once we fetched the data
so in this second, then block here
we wanna transform the data.
And we can do this for creating a helper array here
meetups like this.
And then a four in loop where we go through
all our keys in data.
So all the keys in this data object
we fetch from Firebase.
And these keys will be this
random IDs here.
These unique IDs that will be our keys.
And then we create a new meetup here
for every key through which we loop.
So for every meetup that's stored on Firebase
and we can set the ID equal to key
since that is that auto-generated ID.
And then just distribute data key into this object.
So we access the nested object for the given key.
So we access the nested object here.
And then we just use the spread operator
which is a default JavaScript operator
to copy all the key value pairs of this nested object
into this object.
And that constructs meetup object as we needed.
And then we just need to push that onto
our helper array here.
And then it's this helper meetups array
which we wanna set as our loaded meetups data.
And now if we do that, if we transform the data like this
if we save this and reload, we see loading briefly
but then we see all our meetups.
And that's how we can fetch data in this case from Firebase
but in general with useEffect
fetching data with a loading and a data state
without causing an infinite loop.