Rendering With Asynchronous Actions

Samuel Guo
Nov 4 · 6 min read

Background

Debugging one’s code, especially your own, can get frustrating and tedious some times. It can get even more frustrating if you believe you fully understand the intricacies and the flow of the code itself. A specific occurrence of this scenario is when I kept “randomly” running into an error where it cannot read the property of null during my capstone project. Surprisingly enough, (or not), my colleague also ran into the same issues I did for his side project. Sometimes the code would compile and render as expected and sometimes an error occurs where it cannot read the property. If my colleague and I both ran into the same errors, then this is probably a much more common occurrence than expected.

Re-creating the Bug

As any good debugger, one should be able to re-create the bug to fully understand where everything went wrong. The gists below re-creates the specific bug mentioned in the Background section.

Back end data of User with the id of 1
App.js for re-creating the bug

In the first gist, it renders the data of the user whose id is 1 in JSON format. For reference, this data was created using Ruby on Rails with RESTful conventions. In this gist, the properties I really care about are name, favorite food, and hobbies. I could have prevented the id, created_at, and updated_at properties from passing through by using a serializer but was not necessary for the sake of this example.

In the second gist, this is the front-end code to format and display the data that would be retrieved from the back-end (aka, data from the first gist). In lines 5–7, I instantiated an initial state of user to be null since the data that would populate that state is the user from the fetch request. In lines 9–17, the lifecycle method componentDidMount() is a function to start and finish the fetch request to retrieve data from the back-end (i.e., user data with an id of 1). In lines 19–28, all that does is render specific parts of information from its component state.

Error Error!

By running the front-end code as is, the following error appears:

TypeError: Cannot read property ‘name’ of null

The error appears on line 22, where this.state.user.name is called. Great, now that we re-created this error, its time to compare the thought process, before and after debugging the code.

Initial Thought Process

My first impression of this error, when I first encountered it during my capstone project, was that the error was an unintentional bug, as in a bug with a bug (meta right?). This was because the error “randomly” appeared at times, preventing my code from compiling and rendering whereas for the majority of the time, the code would compile and render as “expected”. The following steps were my thoughts as to why I thought the bug shouldn’t be appearing at all:

  1. I initialized the state as null since the state will be determined from the fetch request (lines 5–7).
  2. Start and complete the fetch request when the component mounts. Once the fetch request is completed, I can do a setState() to update the initial state, which will be null at this point, to the retrieved information (lines 9–17.
  3. Now that the state has all of the necessary information, I can now pick and choose the specific information from the state and render it accordingly (lines 19–28).

Note that the lines mentioned after each step is a reference to the second gist, not to my capstone project. This was to show the parallel thought process between my capstone project and the example provided.

Debugging Time

The error states that it “cannot read the property ‘name’ of null”. This appears on line 22, so where the code reads this.state.user.name , it is stating that this.state.user is null, the original state of the component. As a quick sanity check, I placed a console.log in between render and return (line 20) to see what the state was. The screenshot below is what is shown:

Console logging the component state

In the console, the component state, or this.state , is indeed null. But here’s the interesting part, the console.log appears twice, which implies that there were two separate renderings.

The first rendering of the console.log is when the page first loads. This is before componentDidMount() is invoked. So during this first pass, it is expected for the state to be null because the initial state was initialized as null.

The second rendering of the console.log is after the setState() function is invoked within the componentDidMount(). Although the console.log is saying that the state is null, this is typical because setState() does not immediately update the component. For a more thorough explanation, please see the setState() documentation. Reading the setState() documentation will definitely give you a more holistic explanation and understanding of that function. It also helped me get out of the habitual mindset that code runs in a linear progression rather than a dynamic, non-linear progression.

Although I went on a educational tangent, it still doesn’t help alleviate this error. The root cause of the error still seems that the state is not updated before invoking the render() function. This means that the fetch request is not finished prior to rendering this.state.user.

The Horror of Asynchronous Actions

After contemplating on my original thought process, and banging my head on my desk for several hours, the root cause of the issue was right in front of me. This also goes back to Javascript basics. A fetch request is an asynchronous request, which means it is near impossible, or completely impossible, to determine when this request is completed. In my original thought process, I kept on subconsciously thinking that the rendering will wait until the fetch request is completed, which is NOT how asynchronous actions work.

Since it is impossible to determine when the request will be completed, how do I approach debugging this error? I don’t have control on asynchronous actions, meaning I can’t speed up on completing the fetch request. Also, it doesn’t help that the data is only six attributes, so it doesn’t make sense to try to decrease the amount of data itself in the fetch request (this also won’t work for scaleability purposes as well). I do have control over what gets rendered though.

New Thought Process

So far, I know the error appears because this.state.user is still null when the code hits the render() function. Since the fetch request and setState() are both asynchronous, I don’t know when the state will be updated but I know that when it does, it will re-render the component, in which case this.state.user will not be null anymore. this.state.user will not be null is a safe assumption because this.state isn’t a deeply nested object.

It looks like the main issue now is that the code needs to render something that is not based on this.state.user during the asynchronous time frame. It should be rendering this.state.user.name or something else. One or the other… Hm… This sounds like the job for an if/else statement! Or more specifically, a ternary operator!

As an example, the new code might look something like this: this.state.user ? this.state.user.name : "HEY, I'M STILL WAITING FOR THE ASYNCHRONOUS ACTIONS TO FINISH!" . Logically speaking, this makes sense thus far because if this.state.user is truthy, this means that this.state was updated and can render the necessary information. If this.state.user is falsy, that means the asynchronous actions (i.e., fetch request and this.stateState() ) are not completed, which will then render "HEY, I'M STILL WAITING FOR THE ASYNCHRONOUS ACTIONS TO FINISH!" .

Let’s test this theory out in line 23 and change it into a ternary operator. The gist below shows this change.

Testing with a ternary operator

Below is the screenshot after the ternary operator was implemented.

And it looks like the ternary operator was a success! The error moved from this.state.user.name to this.state.user.favorite_food . Lets implement the ternary operators to wherever this.state.user is called.

This gist above renders without any errors!

NOTE: If you see a flicker while running this code on your browser, this is normal. This is because on the first render, it will render “HEY, I’M STILL WAITING FOR THE ASYNCHRONOUS ACTIONS TO FINISH” and when the state is updated, the component will re-render, which is the cause of the flickering, with the retrieved information from the fetch request.

Key Takeaway

Constantly be aware which functions are asynchronous. By knowing when the asynchronous actions occur, one can plan around it, especially if the results of the asynchronous actions has a direct impact on the rest of the code.

Github Links

Please see the links below for the back-end and front-end repositories. Feel free to tinker around with the code base to help you better understand this blog.

Back-end: https://github.com/guosamuel/rendering_with_asychronous_actions_back_end

Front-end: https://github.com/guosamuel/rendering_with_asychronous_actions_front_end

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade