React Hooks - First Impressions
React hooks is new addition from 16.8 that allows usage of state in pure functions, and enables other stateful logic to be used in function components. This article is structured first to give my impressions at high level, for those who know react hooks, and second to provide react hook basics and technical discussion on implementation, for those who want to learn hooks.
All source code referred in the article is available at GitHub
My first impressions of using React hooks
Following are some of my key observations using react hooks
- Makes easier to use state : Function components can also define, and access state using useState hook. Reduces dependency on component classes.
- Reduces code duplication : Component lifecycle methods leads to repeating code across methods. useEffect hook helps reduce that code duplication making single place for side effects.
- Better state management : useState can be used multiple times to define different state variables. Reduces complex state objects, and brings modularity for state objects.
- Reduces Redux dependency : Dan Abramov, one of the creators of Redux, says - "don't use Redux until you have problems with vanilla React." Introduction of hooks makes using react for state management easier - further delaying need for Redux.
- Increases code reuse and modularity : Custom hooks makes it easier to reuse the stateful logic across various function components.
- Reduces need for passing props : React hooks allow usage of the state wherever it is needed instead of passing it as props to next level components.
These are initial impressions I gathered from using react hooks to build a quick application.
React Hooks - Basics and Technical Discussion
In this section, we will look at some of the key react hooks, react router hooks and write one custom hook. React and React Router prior knowledge is assumed in the discussion in this section.
Full source code of working sample application with react hooks and react router hooks can be found at GitHub. You can use that as starter or as learning resource - feel free to copy and use.
useState - hook from React
useState hook allows to include a state to the function component. Following is the API signature of useState hook.
const [state, setState] = useState(initialState);
useStates take one argument - initial value of state to be setup. This hook return array of two elements - first one, state object itself, and second one, function used to change state.
Let us see this in action in real world code snippet (App.js)
import React, { useEffect, useState } from "react";
//some other code in between..
function App() {
//multiple useState can be defined
let [users, setUsers] = useState(null);
let [isLoading, setisLoading] = useState(true);
let [currentUser, setCurrentUser] = useState(null);
Following key things are achieved in code above
- define "users" as state variable, initialize to null and define setUsers function to change the state
- Similarly, isLoading and currentUser state variables are defined with their initial values
Unlike earlier versions of react, where "this.state.variable" was used, one can directly refer state variables in the function. In case above, users, isLoading and currentUser can be referenced directly in the function.
useEffect - hook from React
useEffect allows side effects, all other activities outside rendering, to be included in function components. useState hook replaces most functions currently done in componentDidMount, componentDidUpdate and componentWillUnmout lifecycle methods, and helps avoid code duplication across these methods.
Following is the API signature of useEffects
useEffect(didUpdate);
useEffect accepts function as input. By default, "didUpdate" function runs after every render.
Let us see useEffect in action (App.js)
useEffect(
() => {
//setting state to true
setisLoading(true);
//fetch sample request from reqres.in for loading users
fetch(`https://reqres.in/api/users`, {
method: "GET"
//other code..
},
//empty array indicates to run useEffects only once
[]
);
In this case, useEffect hooks defines function that does two key things
- sets isLoading state to true
- AJAX call to API (in this case sample api hosted by reqres.in)
Note that second parameter to useEffect function is empty array. This empty array indicates that this function will be executed only once when component is loaded. If second parameter is not added then effects are executed on every render.
One more example added to sample project that uses useEffect (userDetail.jsx)
useEffect(
() => {
//fetch sample request from reqres.in for loading users
fetch(`https://reqres.in/api/users/` + currId, {
method: "GET",
// other code here
},
//Try replacing [currId] with [] below.. server call will not be done after clicking next user details after removal
[currId]
);
UserDetail function component receives selected user as input props, and calls API to get that user details. Second parameter to useEffect, "currId" in brackets, restricts execution of effects until currId is changed. On every change of currId, API will be called again to reload data from server. To demonstrate this - I have added button "See Next User" button to the detail screen. On every click of this button, currId is incremented and new data is loaded from API.
Tip : Try clicking "See Next User" after changing second parameter from [currId] to [] - API call will not be done.
useParams, useHistory, useLocation - hooks from React Router
In the sample application URL pattern /user/:id is used to see user details for user specified by "id". useParams hook from react router helps capture the parameters passed in URL string. useParams usage is demonstrated in the sample application in userDetail.jsx
let { id } = useParams();
Above code extracts "id" parameters defined in the router that invokes UserDetail function component.
useHistory hook gives access to "history" object that can used for navigation. The sample application uses history object to navigate from list of users to user details. Following code snippet shows usage of useHistory for navigation in userList.jsx
//useHistory is hook from react router and gives access to browser history
const history = useHistory( //some z
<Button
variant="contained"
color="primary"
onClick={() => {
props.setCurrentUser(props.user);
history.push("/user/" + props.user.id);
}}
>
See User
</Button>
useLocation hook provides access to location object that represents current URL. This hook can be used to get access of current URL. Sample application implements this hook to demonstrate usage - but we will see this along with custom hook in next section.
Custom Hooks
Custom hooks are defined to extract the common logic in a function so that it can be reused at multiple places. The sample application implements function usePageViews as custom hook in usePageViews.jsx.
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
const usePageViews = () => {
//useLocation hooks gives access to current location object
let location = useLocation();
//useEffect hook is called whenever location changes
useEffect(
() => {
//call your API or google analytics to send information about page loaded
//OR do activity to be done on each page load
console.log("Page Loaded - " + location.pathname);
},
//useEffect called again when location is changed
[location]
);
};
export default usePageViews;
Function usePageViews is used as demonstration that shows whenever "location" is changed then a specific action can be done such as calling google analytics or doing custom actions before new page loads. Look developer tools and console to see that this prints current location whenever location is changed.
Hope this article was useful introducing react hooks, and leave your views/comments.