DEV Community

Cover image for Intro to React Hooks
peytono
peytono

Posted on

Intro to React Hooks

Overview

In React you've always had access to state, a tool that allowed you to hold on to temporary values, non persisted changes, with easy ways to rerender your components to reflect those changes. At first, if needing state in a component, you’d need to take advantage of class components and lifecycle methods. With class components the constructor, render, componentDidMount, etc. control the functionality of a certain lifecycle. Giving you control of what functionality happens over stages of the component. On more complicated components, this gets messy and doesn’t allow for a real separation of concerns. For example, a componentDidUpdate() method would require every functionality needed for any updates to the state. In this article we’ll go over the most common React Hooks.

Introducing hooks! With React Hooks, all components should be functional. This rids the need for all lifecycle methods, including the constructor and render. Hooks allow for statefulness in functional components, as well as more control over your logic flow. All hooks must be imported into the file from React.

useState

The first hook to get acquainted with is useState, instead of adding values to the state object inside of the constructor and using setState, you’ll add useState inside your function statement, but before your return. When calling useState, you’ll use array destructing syntax to declare, the first being the name of your new state, then the update function, conventionally preceding the state name with ‘set’. Then you’ll pass in the initial state value inside the useState invocation like so:

const [user, setUser] = useState({});
Enter fullscreen mode Exit fullscreen mode


Now anytime you need to change the value of the state you’ll use the update function, passing in the new value of state, or a function that resolves to the new value of state. Using the updater function is the same as setState, except the updater function is specific to the updated state.

 const [message, setMessage] = useState(‘’);

const startState = [hello];

const [messages, addMessage] = useState(startState);

const [isWaiting, setWaiting] = useState(false);
Enter fullscreen mode Exit fullscreen mode

In the below example I'll also use axios, a great technology to help make HTTP requests.

const onSend = () => {
  setWaiting(true);

  axios
    .post(/chatbot, { messages: allMessages })
    .then((response) => {
      setWaiting(false)
      addMessage((curMessages) => curMessages.concat([response.data.message]));
      axios.post(/chatbot/db, { message: response.data.message, userId: user.id });
      })
      .catch((err) => console.error(failed sending new message, err));

  setMessage(‘’);
};
Enter fullscreen mode Exit fullscreen mode

useEffect

The second hook to get acquainted with is useEffect, which can take two parameters: the first, a callback function, most commonly an anonymous arrow function, being required, and the second, an array, to include the effects dependencies. Intuitively, whenever useEffect is triggered, the function passed in will be invoked. The dependencies array should consist of any reactive values from the callback. Reactive values include stateful values or other hooks. Meaning, anytime a dependency value changes, that useEffect will be called. useEffect is super helpful in splitting up your component functionality into much more readable and modularized code than using React class components lifecycle methods.

useEffect(() => {
  bottomScroll();
}, [messages]);
Enter fullscreen mode Exit fullscreen mode

In the above example, useEffect, calling bottomScoll, will be invoked when the component renders, as well as anytime the value of messages changes.

useMemo

useMemo mixes functionality of useState, useEffect and a memoize function. If you’ve used a memoized function before, useMemo has a similar purpose, holding onto cached values for optimization.

import React, { useMemo } from react;

function EventList({ events, time }) {
  const eventsNow = useMemo(() => filterEvents(events, time), [events, time]);
}

Enter fullscreen mode Exit fullscreen mode

In this first example, we’re creating a function EventList, which is destructuring events and time. Then we’re creating a variable from useMemo called eventsNow. Since we want to call our function filterEvents passing in arguments, we need to pass it into the body of an arrow function, and because events and time are reactive values, they belong in the dependency array. This allows eventsNow to hold onto the result from filterEvents, then any time one of the dependencies changes, eventsNow will keep the cached value until the filterEvents calculation is completed, then any rerendering will take place.

The second example may seems less useful off the bat, but we’ll see how it comes into play.

const [user, setUser] = useState(null);

const userState = useMemo(() => ({ user, setUser }), [user]);
Enter fullscreen mode Exit fullscreen mode

Here we’re creating a state of user, an updater function of setUser, and an initial state of null.

useMemo requires a function with no arguments, as well as a dependencies array. Dependency rules are the same as useEffect.

We’ll see soon why this was useful, but our userState is now an object containing user, currently null, and setUser, which can change the value of user.

useContext

React’s useContext is similar to useState, except it is accessible to any component, including children, inside its provider. This becomes more useful with deeply nested components or stateful values needed throughout the entire app as you won’t have to pass state down the entire tree of React nodes.

Wanting to having the value of the user throughout the app, we’ll reuse the useMemo example above alongside useContext to create a UserContext and provider.

import React, { createContext, useState, useMemo } from react;

export const UserContext = createContext({});

export function UserContextProvider({ children }) }
  const [user, setUser] = useState(null);

  const userState = useMemo(() => ({ user, setUser }), [user]);
  return (
    <UserContext.Provider value={userState}>
      {children}
    </UserContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now inside App.jsx we’ll make the UserContextProvider a wrapper for all components. Making the context available everywhere.

import React from react;
// imports of all components in return

function App() {
  return (
    <UserContextProvider>
      <Login />
      <HomePage />
      <Profile />
      <Events />
    </UserContextProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

In order to use and update the context inside other components we’d have to do something like the example below. If you don’t need to update the context you can access is with just the first two lines, not needing to extract setUser from userContext.

const userContext = useContext(UserContext);

const { setUser, user } = userContext;

useEffect(() => {
  axios
    .get(/user)
      .then(({ data }) => setUser(data))
      .catch((err) => console.error(failed setting user, err));
}, [setUser]);
Enter fullscreen mode Exit fullscreen mode

Thank you!

Hopefully after this overview of the most common React Hooks you’ll be excited to see all you can do with this super helpful tool. If you don’t find the one that’s quite right for your needs, you can just create your own! This is just one of the amazing tools React has for us.

React Hooks Docs

Top comments (0)