- In a functional component in React
you can register code to be executed
after each time the component is rendered.
So, for example, after the component is rendered,
you could write a message to the log file
or you could send a message to a rest service.
You could update some data in local storage,
typically something non graphical
that you want to do after each render has occurred.
This is called an effect hook
and this is how you do it in your component.
You call React, useEffect, and you pass in a lambda, okay?
And that lambda will be executed after each time
the component is rendered to do some additional work,
not graphical, but some additional non graphical work.
And it'll happen after each rendering,
if you write it like that.
So after this component is rendered, each time,
it'll output "hello" from this effect hook on the console.
Okay? Right.
So, there is another way of using Effect Hook.
You can pass in an optional second parameter.
You can pass in an array of dependencies
such that the effect hook will only be invoked
if one of those dependencies has changed.
Have a look at this example. I've got a component.
It calls React, useEffect.
The first parameter is a lambda.
This is the code that I'd like it to do, but only
if either the minimum value or the maximum value changes.
This is an array of dependencies.
If either of these dependencies change,
then this will be executed.
Okay? But only if the minimum
or the maximum value has changed.
So that's conditional effect hook.
Only execute that code if
either the minimum value or the maximum value has changed
since the last time it was rendered.
Right? Well, I've got an example of effect hooks.
If you go here, Enzyme_EffectHooks/demo-app,
if you open that folder app in command window,
NPM install, and then NPM run, NPM start,
and there's a product list component.
And the product list component receives a function called
onRangeChanged.
It's a callback function.
And the idea is
that the product list will basically define
an effect hook such that,
if the minimum value or the maximum value
in state have changed, it will call that call back.
So let's have a look at the code in product list.
Here's my product list component. My product list,
it receives an array of products.
That's the first parameter.
When you're using type script,
you've gotta say the name of the parameter here
or the name of the properties in there, and then colon,
And then you've gotta give it type information
for each property there.
So it's a bit messy,
but basically, what it's saying is, this function
or this component, it receives a products property,
an array of products, like that.
That's the first property.
And the second property is a callback function.
And that callback function,
it will invoke it, you know, under certain circumstances.
That's the function that needs to be invoked.
And that property there is a function
that takes a string and returns void.
You're basically specifying the signature of
the callback function onRangeChanged,
a function that takes a string doesn't return anything,
presumably it will be just outputs
that string on the console, something like that.
Right? Well, let's talk like what's going on here.
We we're basically setting up the minimum value
and the maximum value in React state.
If the user typed the value into a text box for the minimum,
we update the minimum value in state,
which would cause the component to be re-rendered.
If the user types in a new value for the maximum,
then get that value and send that value into React state
to re-render with the new value for the maximum.
Okay. So nothing new there, but this is the effect.
This is my effect hook.
This is the code that I want to execute,
but only if the minimum value is changed
or the maximum value is changed.
So what it'll do is it says, potentially call this function
after each render, but only if the minimum value
or the maximum value or both have changed.
So if the minimum value has changed or the maximum
it'll basically invoke the callback onRangeChange as a
as a callback function, it'll invoke it.
And you'll say, oh, the range has changed.
The value for the minimum is now something.
And the value for the maximum is now something.
It'll call that function back every time the minimum
or the maximum value was changed.
Okay. So think about this, now.
Our product is component.
When we run it from our top level app,
we need to give it some kind of call back.
We need to say what to do when the range changes.
So let's see how we render that component
from the top level.
That's my app component, my application component.
It says, render the product list, give it the products.
Oh, and here is the callback function.
That's the function it's gonna invoke back.
Okay? So whenever the minimum value
or the maximum value changes within the product list,
it'll callback this function.
And it'll just basically output on the log
whatever we told it to, is calling that function
with a string, which then gets echoed on the console.
Okay. So it called my effect hook in product list.
It called that call back.
And it basically, you know, with this message
the range has changed the minimum
and the maximum are now this.
Right. Well, that's interesting.
Let's see what the application looks like.
If you NPM install and then run the application
let's see what it looks like.
I'm gonna bring up the developer tools,
F12 would do the same.
And I'm gonna look in the console window.
I'm hoping to see console messages appearing
in here when the range has changed.
So let's just clear the list there.
I'm going to change the minimum to be five.
Oh. So my product list realized
that the minimum value had changed, and it called me back.
The effect hook called back
range changed minimum five maximum,
the default maximum is a million,
even though I hadn't typed in a million here,
that's the default value for the maximum.
If I type in a maximum of 500, now,
the way I've written my application
each keystroke is basically detected one by one.
So if I type in 500 here,
it generated the maximum value change to five.
Then it changed to 50.
Then it changed to 500.
So it actually called my callback three times.
Okay. So I could probably prove that, but
but you get the idea?
And if I change the minimum price to be 50,
then it says the minimum price is like that.
And if I get rid of the maximum price,
then it'll default back to a million.
And if I get rid of the minimum price,
then it defaults back to zero.
Okay? So these effect hooks are a useful mechanism
in your component.
You're doing the user interface logic,
but there may be some, you know,
you might want to do some additional work
over and above the graphical user interface like,
I'll put in log messages.
So effect hooks give you a way
of doing extra work after each rendering.
Okay. So we can't, in our tests, directly interact
with effect hooks,
because they're entirely managed by React.
But what we can do is to test
that the callback function is invoked properly.
We can say, assuming that an effect hook has been triggered,
it should call me back,
and it should call me back with this value.
And that's what it means to test effect hooks in React.
Check that the effect hook called some callback function
with the appropriate parameters.
Right. So have a look at this.
I've basically, in my test harness,
I've instantiated my component,
but I've passed in a dummy callback function.
So, you know, if the minimum value changes,
or the maximum value changes,
it's going to be calling this mock function,
and I can check you know,
what was that mock function invoked with?
I can verify that the mock function was invoked
with the correct string,
the minimum value and the maximum value.
So there's my mock callback function.
And here is where I render my component,
and I pass the mock callback into the function.
This is the function I'm saying that it should invoke
if the minimum value or the maximum value changes.
And I can then verify that my mock function was invoked.
We know how to do that.
Right. I've got two tests in my product list, test TSX,
one that will check if the minimum prices change
does it gimme back a callback, and one for the maximum.
Let's have a look at this.
So first of all, I've registered a dummy callback function.
That will be passed into my product list component.
This is what's gonna be called back.
So my product list, if the effect hook triggers,
because the minimum value has changed,
or the maximum value has changed,
this, here, is the function that it's gonna call back.
Okay. So it'll call back into this mock object here, right?
So that's set things up.
Let's emulate the user change in the minimum price.
Okay. So I've emulated the user typing in a minimum price.
The minimum text box, I set the value to a hundred.
Okay. If the minimum value changes, remember the code.
If the minimum value changes,
my effect my effect hook should say,
oh, the minimum value is changed.
It should call the callback
with the range changed to the minimum value
and the maximum value.
Okay.
So let's verify that our range callback function was called.
The range has been changed,
the minimum value and the maximum value.
Remember if the maximum value text box is empty
then that we assume a default value of a million.
Okay. So we can verify the effect has been invoked.
I interacted with the user interface, or simulated it.
My effect took should have realized
that the minimum value has changed.
It should call the callback,
and it should have passed this value in like so.
So I verified
that the components' effect hook has been invoked,
has invoked the callback function correctly.
The second test is basically the same.
Okay. I have a callback function here.
I register, I render my product list component saying,
call it back.
I change the value of max.
According to my effect hook, my effect,
my effect hook says if the max value changes, call back
with this value with that string.
Okay. So it should say the range has changed,
the minimum is zero, and the maximum is whatever value I've
simulated the user input in.
Okay. So that's it.
We just need to run the tests
and cross our fingers, and it works fine.
So just to summarize what happened.
Effect hooks are a mechanism widely used
in React to do some additional work
after your component has been rendered in Enzyme.
We don't have any mechanism to this internal mechanism.
We don't have any access to it, but we can,
we can intercept that callback function.
We can pass it, a dummy callback,
and verify that it's been invoked with a correct parameter.
And that's what we've done.
And it worked.