One place for hosting & domains

      Custom

      Create a Custom useFetch() React Hook

      Introduction

      A custom hook is a JavaScript function with a unique naming convention that requires –

      1. the function name to start with use and
      2. the function may call other Hooks

      The whole idea behind custom hooks is just so that we can extract component logic into reusable functions.

      Often times as we build out React applications, we see ourselves writing almost the same exact codes in two or more different components. Ideally what we could do in such cases would be to extract that recurrent logic into a reusable piece of code (hook) and reuse it where the need be.

      Before hooks, we share stateful logic between components using render props and higher-order components, however, since the introduction of hooks and since we came to understand how neat they make these concepts, it no longer made sense to keep using those. Basically, when we want to share logic between two JavaScript functions, we extract it to a third function possibly because both components and hooks are equally just functions.

      The rationale behind this move is not different from what we have already explained above. Compared to using the native fetch API out of the box, abstracting it into the useFetch hook gives us a one-liner ability, more declarative code style, reusable logic and an overall cleaner code as we’ll see in a minute. Consider this simple useFetch example:

      const useFetch = (url, options) => {
        const [response, setResponse] = React.useState(null);
        useEffect(async () => {
            const res = await fetch(url, options);
            const json = await res.json();
            setResponse(json);
        });
        return response;
      };
      

      Here, the effect hook called useEffect is used to perform major functions —

      1. fetch the data with the native fetch API and
      2. Set the data in the local state of the component with the state hook’s update function.

      Also, notice that the promise resolving happens with async/await.

      The effect hook runs on two occasions — When the component mounts and also when the component updates. What this means is, if nothing is done about the useFetch example above, we will most definitely run into a scary recurrent loop cycle. Why? Because we are setting the state after every data fetch, as a result, when we set the state, the component updates and the effect runs again.

      Obviously, this will result in an infinite data fetching loop and we don’t want that. What we do want, is to only fetch data when the component mounts and we have a neat way of doing it. All we have to do is provide an empty array as a second argument to the effect hook, this will stop it from activating on component updates but only when the component is mounted.

        useEffect(async () => {
            const res = await fetch(url, options);
            const json = await res.json();
            setResponse(json);
        }, []); 
      

      The second is an array containing all the variables on which the hook depends on. If any of the variables change, the hook runs again, but if the argument is an empty array, the hook doesn’t run when updating the component since there are no variables to watch.

      You may have noticed that in the effect hook above, we are using async/await to fetch data. However, according to documentation stipulations, every function annotated with async returns an implicit promise. So in our effect hook, we are returning an implicit promise whereas an effect hook should only return either nothing or a clean-up function.

      So by design, we are already breaking this rule because —

      1. We are not returning nothing
      2. A promise does not clean up anything

      As a result, if we go ahead with the code as is, we will get a warning in the console pointing out the fact that useEffect function must return a cleanup function or nothing.

      Warning: An effect function must not return anything besides a function, which is used for clean-up.
      
      It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately:
      
      
      useEffect(() => {
        async function fetchData() {
          // You can await here
          const response = await MyAPI.getData(someId);
          // ...
        }
        fetchData();
      }, [someId]); // Or [] if effect doesn't need props or state
      
      Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching
      

      Simply put, using async functions directly in the useEffect() function is frowned at. What we can do to fix this is exactly what is recommended in the warning above. Write the async function and use it inside the effect.

      React.useEffect(() => {
          const fetchData = async () => {
            const res = await fetch(url, options);
            const json = await res.json();
            setResponse(json);
          };
          fetchData();
        }, []);
      

      Instead of using the async function directly inside the effect function, we created a new async function fetchData() to perform the fetching operation and simply call the function inside useEffect. This way, we abide by the rule of returning nothing or just a cleanup function in an effect hook. And if you should check back on the console, you won’t see any more warnings.

      One thing we haven’t mentioned or covered so far is how we can handle error boundaries in this concept. Well, it’s not complicated, when using async/await, it is common practice to use the good old try/catch construct for error handling and thankfully it will also work for us here.

      const useFetch = (url, options) => {
        const [response, setResponse] = React.useState(null);
        const [error, setError] = React.useState(null);
        React.useEffect(() => {
          const fetchData = async () => {
            try {
              const res = await fetch(url, options);
              const json = await res.json();
              setResponse(json);
            } catch (error) {
              setError(error);
            }
          };
          fetchData();
        }, []);
        return { response, error };
      };
      

      Here, we used the very popular JavaScript try/catch syntax to set and handle error boundaries. The error itself is just another state initialized with a state hook so whenever the hook runs, the error state resets. However, whenever there is an error state, the component renders feedback to the user or practically you can perform any desired operation with it.

      You may already know this, but I still feel that it’ll be helpful to point out that you can use hooks to handle loading states for your fetching operations. The good thing is, It’s just another state variable managed by a state hook so if we wanted to implement a loading state in our last example, we’ll set the state variable and update our useFetch() function accordingly.

      const useFetch = (url, options) => {
        const [response, setResponse] = React.useState(null);
        const [error, setError] = React.useState(null);
        const [isLoading, setIsLoading] = React.useState(false);
        React.useEffect(() => {
          const fetchData = async () => {
            setIsLoading(true);
            try {
              const res = await fetch(url, options);
              const json = await res.json();
              setResponse(json);
              setIsLoading(false)
            } catch (error) {
              setError(error);
            }
          };
          fetchData();
        }, []);
        return { response, error, isLoading };
          };
      

      We cannot complete this tutorial without working on a hands-on demonstration to put everything we’ve talked about in practice. Let’s build a mini-app that will fetch a bunch of dog images and their names. We’ll use useFetch to call the very good dog API for the data we’ll need for this app.

      First, we define our useFetch() function which is exactly the same as what we did before. We will simply reuse the one we created while demonstrating error handling above to explain the data fetching concept in practice since it already has most of the things we’ll need.

      const useFetch = (url, options) => {
        const [response, setResponse] = React.useState(null);
        const [error, setError] = React.useState(null);
        React.useEffect(() => {
          const fetchData = async () => {
            try {
              const res = await fetch(url, options);
              const json = await res.json();
              setResponse(json);
            } catch (error) {
              setError(error);
            }
          };
          fetchData();
        }, []);
        return { response, error };
      };
      

      Next, we create the App() function that will actually use our useFetch() function to request for the dog data that we need and display it on screen.

      function App() {
        const res = useFetch("https://dog.ceo/api/breeds/image/random", {});
        if (!res.response) {
          return <div>Loading...</div>
        }
        const dogName = res.response.status
        const imageUrl = res.response.message
        return (
          <div className="App">
            <div>
              <h3>{dogName}</h3>
              <div>
                <img src={imageUrl} alt="avatar" />
              </div>
            </div>
          </div>
        );
      }
      

      Here, we just passed the url into the useFetch() function with an empty options object to fetch the data for the cat. It’s really that simple, nothing elaborate or complex. Once we’ve fetched the data, we just extract it from the response object and display it on screen. Here’s a demo on Codesandbox:

      Data fetching has always been an issue to contend with when building frontend-end applications, this is usually because of all the edge cases that you will need to account for. In this post, we have explained and made a small demo to explain how we can declaratively fetch data and render it on screen by using the useFetch hook with the native fetch() API.

      Creating A Laravel 404 Page Using Custom Exception Handlers

      Introduction

      PHP Exceptions are thrown when an unprecedented event or error occurs. As a rule of thumb, an exception should not be used to control the application logic such as if-statements and should be a subclass of the Exception class.

      Being unprecedented, an exception can be thrown at any point or time of our application.

      Laravel provides a convenient exception handler class that checks for all exceptions thrown in a Laravel application and gives relevant responses. This is made possible by the fact that all Exceptions used in Laravel extend the Exception class.

      One main advantage of having all exceptions caught by a single class is that we are able to create custom exception handlers that return different response messages depending on the exception.

      In this tutorial, we will look at how to create a custom exception handler in Laravel 5.2 and how to return a 404 page depending on the Exception.

      In Laravel 5.2, all errors and exceptions, both custom and default, are handled by the Handler class in app/Exceptions/Handler.php with the help of two methods.

      The report method enables you to log raised exceptions or parse them to error logging engines such as bugsnag or sentry which we will not delve into in this tutorial.

      The render method responds with an error message raised by an exception. It generates an HTTP response from the exception and sends it back to the browser.

          
          public function render($request, Exception $e)
          {
              return parent::render($request, $e);
          }
      

      We can however override the default error handling with our own custom exception handler.

      
      public function render($request, Exception $e)
      {
          if ($e instanceof CustomException) {
              return response()->view('errors.custom', [], 500);
          }
      
          return parent::render($request, $e);
      }
      

      Under the hood, Laravel does its own handling checks to determine the best possible response for an exception. Taking a look at the parent class (Illuminate\Foundation\Exceptions\Handler), the render method generates a different response depending on the thrown Exception.

          
          public function render($request, Exception $e)
          {
              if ($e instanceof HttpResponseException) {
                  return $e->getResponse();
              } elseif ($e instanceof ModelNotFoundException) {
                  $e = new NotFoundHttpException($e->getMessage(), $e);
              } elseif ($e instanceof AuthenticationException) {
                  return $this->unauthenticated($request, $e);
              } elseif ($e instanceof AuthorizationException) {
                  $e = new HttpException(403, $e->getMessage());
              } elseif ($e instanceof ValidationException && $e->getResponse()) {
                  return $e->getResponse();
              }
      
              if ($this->isHttpException($e)) {
                  return $this->toIlluminateResponse($this->renderHttpException($e), $e);
              } else {
                  return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
              }
          }
      

      In this section, we will create an inbuilt Laravel error by intentionally raising an exception.

      To do this, we will try to fetch records that do not exist from a model using the firstOrFail() Eloquent method.

      Go ahead and set up a simple SQLite database. Luckily, Laravel ships with a User model and a corresponding users table. Simply do the following.

      1. Create a new Laravel project.
      2. Update your .env file to have DB_CONNECTION to be sqlite and the only database parameter.
      3. Create a database.sqlite file in the database directory. This is the default SQLite database as configured in config/database.php
      4. Run php artisan migrate on the route of your Laravel project. This will set up a users table in the database.

      We will then add a route and a controller to get the first user in our users table who just so happens not to exist.

      app/Http/routes.php

      Route::get('/user', [
          'uses' => 'SampleController@findUser',
          'as' => 'user'
      ]);
      

      App/Http/Controllers/SampleController.php

           
          public function findUser()
          {
              $user = User::firstOrFail();
              return $user->toArray();
          }
      

      Running this on the browser will return a ModelNotFoundException error response.

      NotFoundHttpException

      ModelNotFoundException

      With this exception, we can now add a custom handler that returns our own error message.

      We will modify the render method in app/Exceptions/Handler.php to return a JSON response for an ajax request or a view for a normal request if the exception is one of ModelNotFoundException or NotFoundHttpException.

      If it is neither of the two, we will let Laravel handle the exception.

         
          public function render($request, Exception $e)
          {
              
              if ($e instanceof ModelNotFoundException) {
                  
                  if ($request->ajax()) {
                      return response()->json(['error' => 'Not Found'], 404);
                  }
      
                  
                  return response()->view('errors.missing', [], 404);
              }
      
              return parent::render($request, $e);
          }
      

      Add a 404.blade.php file in resources/view/errors to contain our user feedback.

      <!DOCTYPE html>
      <html>
      <head>
          <title>User not found.</title>
      </head>
      <body>
          <p>You broke the balance of the internet</p>
      </body>
      </html>
      

      If we now refresh the page, we have the following message on our view with a 404 status code.

      Custom exception handling with 404 status code feedback

      NotFoundHttpException

      When a user visits an undefined route such as /foo/bar/randomstr1ng, a NotFoundHttpException exception, which comes as part of the Symfony package, is thrown.

      To handle this exception, we will add a second condition in the render method we modified earlier and return a message from resources/view/errors/missing.blade.php

      
          public function render($request, Exception $e)
          {
              
              
              if ($e instanceof ModelNotFoundException or $e instanceof NotFoundHttpException) {
                  
                  if ($request->ajax()) {
                      return response()->json(['error' => 'Not Found'], 404);
                  }
      
                  
                  return response()->view('errors.missing', [], 404);
              }
      
              return parent::render($request, $e);
          }
      

      Just like we did in the previous section, Laravel 5.2 makes it all too easy to create custom error pages based on the exception that was thrown.

      We can also simply generate a 404 error page response by calling the abort method which takes an optional response message.

      abort(404, 'The resource you are looking for could not be found');
      

      This will check for a corresponding resources/view/errors/404.blade.php and serve an HTTP response with the 404 status code back to the browser. The same applies to 401 and 500 error status codes.

      Depending on an application’s environment, you may want to show varying levels of error details. You can set the APP_DEBUG value in config/app.php to either true or false by changing it in your .env file.

      In most cases, you may not want your users in production to see detailed error messages. It is therefore good practice to set APP_DEBUG value to false while in a production environment.