A closer look at useEffect — Part 1

In a previous article (link here), I took a deeper dive into React’s useState hook and promised I would do the same with the useEffect hook. I’ll break this examination into two parts, since I want to deal with a couple odds and ends that relate to a small widget I built to illustrate useEffect.

Ok, so, to illustrate this hook, we’re going to build a simple search widget that calls on the Wikipedia API.

Simple Search component using useState hook

We now need to add in some code to take the user’s search term and send a request off to the Wikipedia API. There are actually 2 possible options as to where to write the code to make that request.

In this option, the user types into the text input and we immediately make a request to the API. We do this by adding in some code to the onChange event handler; inside this event handler, we take the value from the input to make the request, which takes some amount of time. After this short period of time, we then get a response; we extract some data out of that response and we use that to update some piece of state, which we can call results. The updated piece of state contains the list of search results, our component rerenders and we render out a stylized list of those results.

Sounds great, right? For the most part, yes; unfortunately, there is a slight downside to this option, which becomes clearer after we examine Option #2.

Here, the user types in an input. The onChange event handler is called and this will update our term piece of state, causing our component to rerender.

The next step is the key to this option (notice the green text color and the big stars). Inside of our component, we can add some code to detect that our component rerendered and our term piece of state changed! Once we detect that, we can then make a request to the API…and the remaining steps are the same as in option #1. After a brief period of time, we get results, we can update the results piece of state, our component rerenders again and we show our results to the user.

The question then becomes: do we make the API request immediately in the onChange event handler OR do we only want to update some piece of state and make the request only after our component begins to rerender and we detect our term has changed?

For Option #1, the fact that we can search instantly when the onChange event triggers seems like a very big plus. However, as I mentioned above, there is a downside to tightly coupling the onChange event with the search.

For Option #2, the search happens when some piece of state changes. This is VERY beneficial in that we can easily trigger a search when other parameters besides our term change. For example, let’s say we wanted to add some kind of category selector or filter in to our search form…we could easily do that by selecting option #2. Indeed, with this option, it is much easier to extract code out into a more reusable function!

Wonderful! Fantastic! We’ve answered that question. But what does this have to do with the useEffect hook?? Well, that add-code-to-detect-term-has-changed part…that’s what useEffect allows us to do! The useEffect hook allows us to write out some code that detects our component is rerendering and some piece of information has changed.

useEffect

We have to keep in mind that we are using a Functional Component here, which does not naturally have access to lifecycle methods. But we will still want to make use of code that runs at particular times, so this is when we can implement the useEffect hook; this hook allows for Functional Components to take advantage of things that are very similar to lifecycle methods (it’s important to remember that these are not the ACTUAL lifecycle methods).

We can configure useEffect to run some code automatically in one of three scenarios:

  1. When the component is rendered for the first time only
  2. When the component is rendered for the first time and whenever it rerenders
  3. When the component is rendered for the first time and…the next two things must happen together…whenever it rerenders and some piece of data has changed.

useEffect takes two arguments. The first argument is always a function. It is the second argument that determines which scenario (1, 2, or 3 above) will be implemented. The three options we can have are:

  1. an empty array — this leads to scenario 1 from above
  2. an array with some value (or values) inside of it — this leads to scenario 3 from above
  3. no argument at all — this leads to scenario 2 from above

Ok, so let’s implement everything we’ve discussed so far.

In the code above, we’ve provided an array with a value (our ‘term’ piece of state) inside of it for our useEffect.

So, according to our code above, the function we are calling within useEffect will run when our component renders for the first time AND when it rerenders IF our term piece of state has changed. This essentially mimics our componentDidMount lifecycle method, if we were calling setState within that method.

I wanted to mention a word of caution when using axios and useEffect, which I discovered while playing around with this widget. We are not allowed to mark the function we are passing into useEffect as async and use any await keyword directly inside that function!

There are a few solutions to this problem, one of which includes using promises, but React actually provides a suggestion with the error message that it spits out.

React’s suggested solution

React suggests creating a helper function within the function called by useEffect. That function can be marked as async and we can use the await keyword. We then invoke that helper function within useEffect. We’re going to also include an options object within that call, to which we will give a params property and assign it an object. Whatever key/value pairs we put inside that object, axios will code them into a query string and append it on to the end of the url automatically.

Another little aside here. Notice that when I set up my term piece of state, I gave it a default value of ‘dog’.

‘dog’ is the default value for my term piece of state

And in the params object of my axios call, I included the reasoning why I put a default value to begin with.

Params object of my axios call within my useEffect

Because useEffect gets called immediately upon the initial render of the component, there will not be a value for the term piece of state at that point…the user just opened the page and so hasn’t typed anything in yet! If there is no default term, React will throw an error.

Instead of including a default value for term, I could have included logic within useEffect to invoke the search function ONLY if there is a value for term. In that case, the end of the useEffect block of code would look like this:

if (term) {
search();
}
}, [term];

Ok, almost there.

Finally, we need to map over the results piece of state to build out our list of search results for display (with some very basic Semantic UI styling).

Code immediately following useEffect block

Very last aside for this article. See that dangerouslySetInnerHTML weirdness? What’s that all about?

Well, the Wikipedia API is a bit quirky and it actually gives us results with HTML in them; the purpose of this, on Wikipedia’s end, is to highlight the search term by using <span class=”searchmatch”></span>. But it looks very unpleasant for our purposes.

Ew, gross.

How can we get rid of this? Well, we can write in some logic to search for the presence of a <span> and remove it. However, there is a ‘hidden’ feature in React that does the trick as well, the dangerouslySetInnerHTML! You provide it with an object whose key MUST be double-underscore html (__html) and the value set to whatever you are trying to render. I wanted to include it here because it was something surprising that I discovered! That being said, I also discovered that it’s NOT something you should use frequently; and certainly NOT if you do not trust the site you are querying. Why? Well, anytime you take a string from a third party, you could be introducing a security hole into your app…in particular, one called an XSS attack, which stands for a cross site scripting attack. This is where we accidentally pick up and render some html from an untrusted source, and this could allow someone to execute some javascript inside of our app. So use that dangerouslySetInnerHTML carefully, if at all!

At this point, then, we have basic search functionality with our useEffect hook.

List of results from the Wikipedia API based on user input

But if we actually want to see each entry in its entirety, we’ll have to do a bit more. And that will be the subject of my next article!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store