Categories
React

How to Register Event Handlers with React Hooks?

React hooks are the main way that we can create components.

Sometimes we have to register event handlers in our own components.

In this article, we’ll look at how to register native event handlers within React components.

Registering Native Event Handlers

We can register native event handlers with vanilla JavaScript.

We can call the addEventListener method with a DOM element, window or document to register an event handler.

For instance, we can write:

import React, { useEffect } from "react";

export default function App() {
  const handleUserKeyPress = (e) => {
    console.log(e.which);
  };

  useEffect(() => {
    window.addEventListener("keydown", handleUserKeyPress);

    return () => {
      window.removeEventListener("keydown", handleUserKeyPress);
    };
  }, []);

  return <div></div>;
}

to register an event handler for the window ‘s keydown event.

We just call addEventListener to add the event listener.

And to remove the event listener when the component unmounts, we return a function that calls window.removeListener to remove the keydown event listener.

The function we return in the useEffect callback only runs when the component unmounts.

The 2nd argument we passed into the useEffect hook is an empty array, so the useEffect callback runs only when we mount the component.

This should only be used for objects like window and document which aren’t part of the component since we can assign event handlers for them by assigning the correct props for the event handler.

Conclusion

We can register native event handlers for window or document in the useEffect gook by calling addEventListener .

And to remove the event listener when the component unmounts, we return a function that calls removeEventListener to in the useEffect callback to remove the event listener when the component unmounts.

Categories
React

How to Assign Multiple Refs for an Array of Elements with React Hooks?

In React components, we can assign refs to elements in a component so that we can get access to them in the component code.

Sometimes, we have multiple elements that we want to get access to in our code via refs.

In this article, we’ll look at how we can assign multiple refs to an array of elements with React hooks.

Assign Multiple Refs for an Array of Elements

One way to assign refs for multiple elements rendered from an array is to store the refs in a state.

For instance, we can write the following:

import { createRef, useEffect, useState } from "react";

const arrLength = 5;
export default function App() {
  const [elRefs, setElRefs] = useState([]);
  console.log(elRefs);

  useEffect(() => {
    setElRefs((elRefs) =>
      Array(arrLength)
        .fill()
        .map((_, i) => elRefs[i] || createRef())
    );
  }, []);

  return (
    <div>
      {Array(arrLength)
        .fill()
        .map((el, i) => (
          <div ref={elRefs[i]} key={i}>
            {i}
          </div>
        ))}
    </div>
  );
}

We defined the elRefs state which has an empty array as its initial value.

Then we add the useEffect callback that calls setElRefs to populate the elRefs state with an array of refs.

To do this, we create an empty array with length arrLength .

Then we call fill to fill the empty slots so we can call map on it to add the refs.

Then we call map with a callback to either return the existing ref or call createRef to create a new ref and return it.

The empty array in the 2nd argument of useEffect indicates that we only run the callback once when we mount the component.

Below that, we render an array with length arrLength to divs by calling map with a callback to return a div with the ref assigned to it.

We assign a ref to each div rendered with the ref prop.

We just pass in the elRefs[i] value to it to assign the ref.

The key prop has to be set to a unique value so that React can identify the rendered elements properly.

Now when we log the value of elRefs as we did in App , we should see 5 objects in an array with the current value of each set to the element that we rendered.

Alternatively, we can create a ref with the useRef hook and set the current value to an array.

Then we can populate the array with the elements.

To do this, we write:

import React, { useRef } from "react";

const arrLength = 5;
export default function App() {
  const refs = useRef();
  refs.current = [];
  console.log(refs);

  const addToRefs = (el) => {
    if (el && !refs.current.includes(el)) {
      refs.current.push(el);
    }
  };

  return (
    <div>
      {Array(arrLength)
        .fill()
        .map((el, i) => (
          <div ref={addToRefs} key={i}>
            {i}
          </div>
        ))}
    </div>
  );
}

We set the refs.current value to an empty array.

Then we define the addToRefs function to call refs.current.push with el to add el to the refs.curent array.

el is the element which is assigned the addToRefs function to the ref prop.

So el would have the rendered divs since they have the ref prop set to addToRefs .

Therefore, refs.current is an array with all the rendered elements.

Conclusion

We can create refs and assign them to multiple elements or create a single ref with an array and add all the elements object to it in a React component.

Categories
React

How to Make the useEffect Hook Not Run on Initial Render?

The useEffect callback runs whenever the states we’re watching are updated, when a re-rendering is done, or when the component mounts.

However, sometimes, we may want to make it run only after the initial render is done.

In this article, we’ll look at how to make the useEffect hook callback run only after the first render.

Make the useEffect Hook Not Run on Initial Render

We can make the useEffect hook not run on initial render by creating a variable with useRef hook to keep tracking of when the first render is done.

We set the variable’s value to true initially.

Then we the component is rendered the first time, we set the variable to false .

To do this, we write:

import { useEffect, useRef, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  const firstUpdate = useRef(true);
  useEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
      return;
    }
    console.log(count);
  });

  return (
    <div>
      <button
        onClick={() => {
          setCount((count) => count + 1);
        }}
      >
        increment
      </button>
      <p>{count}</p>
    </div>
  );
}

We have the count state which is initially set to 0.

Then we have the useEffect hook called with a callback that gets the firstUpdate.current property.

If it’s true , then first render isn’t done yet since we created the firstUpdate variable with useRef and set its current value to true initially.

Therefore, if it’s true , then we set firstUpdate.current to false and stop running the rest of the code.

Otherwise, we log the value of count .

We didn’t pass in a second argument into useEffect , so the callback runs on every render.

Below that, we have a button with an onClick prop set to a function that calls setCount with a callback to increment count by 1.

And below that, we show the count value.

Now in the console, we should see that 0 isn’t logged, so we know that the code after the if block in the useEffect callback didn’t run during the first render.

Conclusion

To prevent the useEffect callback code from running on initial render of a component, we can create a variable to keep track of whether the first render of a component is done.

We only proceed on running the remainder of the useEffect callback only when the variable value indicates that the first render has been done.

Categories
React

How to Reload State from Props in a Component Created with React Hooks?

Sometimes, we’ve to reload a state according to a prop value that’s passed in.

In this article, we’ll look at how to reload states from props in a component created with React hooks.

Watch Props Change with useEffect

The useEffect hook lets us watch for changes of reactive values, which includes props.

Therefore, we can use the useEffect hook to watch for changes of prop values and update our state accordingly.

For instance, we can write:

import { useEffect, useState } from "react";

const Counter = ({ count }) => {
  const [countState, setCountState] = useState(count);

  useEffect(() => {
    setCountState(count);
  }, [count]);

  return <p>{countState}</p>;
};

export default function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <button onClick={() => setCount((c) => c + 1)}>increment</button>
      <Counter count={count} />
    </div>
  );
}

We have the Counter component that takes the count prop.

Inside the component, we have the countState state that’s set to count ‘s value as its initial value.

Below that, we call the useEffect hook with a callback that calls setCountState with count to set countState ‘s value to be the same as count ‘s value.

In the 2nd argument of useEffect , we pass in an array with the count prop to watch for changes in the count prop and trigger the useEffect callback to be called.

Then in the App component, we have the count state initially set to 0.

And we have a button that calls a function that calls setCount to update the count state by incrementing it by 1 with the callback we pass in.

The callback gets the current value of count as the parameter and returns the new value which has been increased by 1.

Below the button we show the Counter component where we pass in the count prop by setting it to the count state as its value.

Now when we click the increment button, we see the latest countState rendered via the count prop.

Conclusion

We can update a state according to changes of a prop by watching it with the useEffect hook and then calling the state change function to update the state according to the prop value in the useEffect callback.

Categories
React

How to Force a Component to Re-render with React Hooks?

In React class components, the forceUpdate method is provided for forcing the re-rendering of a component.

It’s handy for situations where we want to update a component in response to changes external to the component.

However, there’s no equivalent if we use function components with React hooks.

In this article, we’ll look at how to force the re-rendering of a component created with React hooks.

useCallback Hook

One way to create a forceUpdate function from React hooks is to use the useCallback function.

For instance, we can write:

import { useCallback, useState } from "react";

let time = 0;
setInterval(() => {
  time++;
}, 1000);

export default function App() {
  const [, updateState] = useState();
  const forceUpdate = useCallback(() => updateState({}), []);

  return (
    <div className="App">
      <button onClick={() => forceUpdate()}>force update</button>
      <p>{time}</p>
    </div>
  );
}

We have the time variable that we want to render in the App component.

But since it’s not reactive, it won’t show the latest value unless we trigger it to re-render manually.

To create our forceUpdate function, we create the a reactive state with the useState hook.

We didn’t assign the returned state into a variable.

But we did assign the state change function to a variable.

upateState is the function that we can call to force a re-render.

Next, we call the useCallback hook to create a function that calls updateState with a callback that calls updateState .

useCallback lets us cache the function so that it won’t be created repeatedly after each re-render.

updateState will trigger an update since it changes a reactive state.

We have a button that has an onClick handler.

onClick is set to a function that calls forceUpdate to force a re-render.

Once we click the button, we should see the latest value of time at the time we clicked the button.

When we click it again, we’ll see the latest value of time rendered.

If we want, we can also pass in a callback into our updateState function:

import { useCallback, useState } from "react";

let time = 0;
setInterval(() => {
  time++;
}, 1000);

export default function App() {
  const [, updateState] = useState(0);
  const forceUpdate = useCallback(() => updateState((tick) => tick + 1), []);

  return (
    <div className="App">
      <button onClick={() => forceUpdate()}>force update</button>
      <p>{time}</p>
    </div>
  );
}

This will also trigger a state update so a re-render will be done.

Conclusion

We can force a function component created with React hooks to re-render by updating a state.