Categories
React

How to Reset to Initial State with React Hooks?

Resetting our components to their initial states is something that we want to do often.

In this article, we’ll look at how to do with components that are created with React hooks.

Resetting States to Initial State

To reset states of a component to their initial state, we can store our states in one object state.

Then we can reset them all at once easily.

For instance, we can write:

import { useState } from "react";

const initialState = {
  username: "",
  email: "",
  password: "",
  passwordConfirmation: ""
};

export default function App() {
  const [
    { username, email, password, passwordConfirmation },
    setState
  ] = useState(initialState);

  const clearState = () => {
    setState({ ...initialState });
  };

  const onChange = (e) => {
    const { name, value } = e.target;
    setState((prevState) => ({ ...prevState, [name]: value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    setTimeout(() => {
      clearState();
    }, 1000);
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Username:
          <input value={username} name="username" onChange={onChange} />
        </label>
      </div>
      <div>
        <label>
          Email:
          <input value={email} name="email" onChange={onChange} />
        </label>
      </div>
      <div>
        <label>
          Password:
          <input
            value={password}
            name="password"
            type="password"
            onChange={onChange}
          />
        </label>
      </div>
      <div>
        <label>
          Confirm Password:
          <input
            value={passwordConfirmation}
            name="passwordConfirmation"
            type="password"
            onChange={onChange}
          />
        </label>
      </div>
      <button>Submit</button>
    </form>
  );
}

We have the initialState object with the initial state of our sign up form.

Then we call useState in App to create the object state.

Next, we create the clearState function to reset the state by calling the setState state setter function with a copy of the initialState .

Making a copy lets React know that it has to update the state and re-render.

The handleSubmit function calls e.preventDefault to do client-side submission.

Then we call clearState in the setTimeout callback to reset the state to the initial state.

And finally, we have the form inputs to enter all the data.

value is set to the properties.

And onChange is set to the onChange function, which calls setState with a callback that returns a copy of the state and the new property value to update the object state.

This way, the form inputs are filled with what we entered.

Conclusion

We can reset a component to their initial state easily if we store our states in one object.

Categories
React

How to Add Throttle or Debounce with React Hooks?

Throttling and debouncing let us slow down the running of code in different ways.

Debounce means we run a piece of code after a given timeout interval.

Throttling means that we don’t let a piece of code run more than one in a given period.

In this article, we’ll look at how to add throttle and denounce to our React components created with React hooks.

Debounce

We can run a piece of code once after a given time interval by adding a timer to throttle the number of times a piece of code runs within a period.

For example, we can write:

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

const useDebouncedEffect = (effect, delay, deps) => {
  const callback = useCallback(effect, [...deps, effect]);

  useEffect(() => {
    const handler = setTimeout(() => {
      callback();
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [callback, delay]);
};

export default function App() {
  const [value, setValue] = useState(0);

  useDebouncedEffect(() => console.log(value), 1000, [value]);

  return (
    <>
      <button onClick={() => setValue(value + 1)}>increment</button>
      <p>{value}</p>
    </>
  );
}

to create the useDebouncedEffect hook to let us run the the effect function with a delay.

It takes the effect callback function, the delay value in milliseconds, and the deps array of dependencies to watch to update the callback.

In the hook, we call useCallback with effect and depts to create a function that’s re-created with deps or effect changes.

Then we call useEffect with a callback to call callback with a delay with setTimeout .

And we return a function that calls clearTimeout to clear the timer.

We watch the callback and delay in the useEffect hook to run the useEffect callback when they change.

In App , we create the value state.

Then we call the useDebouncedEffect hook with the callback to do what we want.

Then we pass in the delay and the array of values to watch.

And then we display a button to update value when we click it and display the value itself.

Throttle

To throttle the number of times a function is run, we can use Lodash’s throttle function.

For instance, we can write:

import { useEffect, useState } from "react";
import _ from "lodash";

export default function App() {
  const [value, setValue] = useState(0);

  useEffect(() => {
    const log = _.throttle(() => console.log(value), 1000);
    log();
  }, [value]);

  return (
    <>
      <button onClick={() => setValue(value + 1)}>increment</button>
      <p>{value}</p>
    </>
  );
}

We create the log function with the throttle function with the callback we want to run.

The 2nd argument is the time interval in which the function runs once in milliseconds.

Conclusion

We can add throttling and debouncing easily with React hooks and Lodash.

Categories
React

Using the useCallback and useMemo Hooks in React Components

The useCallback and useMemo hooks are 2 more advanced hooks that are added in addition to the basic useState and useEffect hooks.

They’re both useful in different ways.

In this article, we’ll look at how to use the useCallback and useMemo hooks in our React components.

useCallback

The useCallback hook is used for memoizing callbacks that we may call multiple times.

Using the useCallback hook, we can cache callbacks that are in a component so that they won’t be created more than once unless a state or prop changes.

For instance, instead of writing:

import React from "react";

export default function App() {
  const handleClick = () => {
    console.log("Clicked");
  };
  return <button onClick={handleClick}>Click Me</button>;
}

We can write:

import React, { useCallback } from "react";

export default function App() {
  const handleClick = useCallback(() => console.log("Clicked"), []);

  return <button onClick={handleClick}>Click Me</button>;
}

We pass in our callback to the useCallback hook so that the function we passed in the first argument is called when we click on the button.

But the handleClick function is only created once instead of every render.

The 2nd argument is an array where we can pass in the items that we can watch in the function.

For instance, if we write:

import React, { useCallback, useState } from "react";

export default function App() {
  const [num, setNum] = useState(0);

  const increment = useCallback(() => setNum((c) => c + 1), []);
  const handleClick = useCallback(() => console.log(num), []);

  return (
    <>
      <button onClick={increment}>Increment</button>
      <button onClick={handleClick}>Click Me</button>
    </>
  );
}

The handleClick function will always log 0 since the function is only created when the component is mounted as we have an empty array in the 2nd argument.

However, if we pass in [num] to useCallback :

import React, { useCallback, useState } from "react";

export default function App() {
  const [num, setNum] = useState(0);

  const increment = useCallback(() => setNum((c) => c + 1), []);
  const handleClick = useCallback(() => console.log(num), [num]);

  return (
    <>
      <button onClick={increment}>Increment</button>
      <button onClick={handleClick}>Click Me</button>
    </>
  );
}

then the console log shows the latest value of num when we click on Click Me.

This is because the handleClick function is re-created when num changes since we passed in [num] in the 2nd argument of useCallback .

useMemo

The useMemo hook lets us compute a value that’s derived from existing states when any of the states that it depends on changes.

For instance, we can write:

import React, { useCallback, useMemo, useState } from "react";

export default function App() {
  const [num1, setNum1] = useState(0);
  const [num2, setNum2] = useState(0);

  const increment1 = useCallback(() => setNum1((c) => c + 1), []);
  const increment2 = useCallback(() => setNum2((c) => c + 1), []);
  const sum = useMemo(() => num1 + num2, [num1, num2]);

  return (
    <>
      <button onClick={increment1}>increment 1</button>
      <button onClick={increment2}>increment 2</button>
      <p>num1: {num1}</p>
      <p>num2: {num2}</p>
      <p>sum: {sum}</p>
    </>
  );
}

We have the num1 and num2 states which are changed when we click the buttons.

Then we define the sum variable, which is assigned from the useMemo hook.

We pass in a function that returns num1 and num2 added together.

In the 2nd argument of useMemo , we pass in num1 and num2 , which we’re watching.

It means that when num1 or num2 changes, the returned value of useMemo callback will be assigned to sum .

Therefore, when we click on either button, sum changes.

Conclusion

We can use useCallback to cache functions and only recreate them when a state or prop changes.

The useMemo lets create variables derived from state or prop values and cache them until any dependent value changes.

Categories
React

Run Async Code on Update of a State with React Hooks

In class-based React components, we can pass a callback into the 2nd argument of setState to run code when a state is updated with setState .

With React hooks, we no longer have the setState method.

Instead, we use state updater functions created with the useState hook to update states.

This means we’ve to find new ways to run code after a state is updated.

In this article, we’ll look at how to run async code after a state is updated in function components created with React hooks.

useEffect Hook

The useEffect hook lets us commit side effects in our component code.

We can commit side effects in response to changes in one or more states.

To specify which state to watch, we pass an array of states we want to watch into the 2nd argument of useEffect .

Then in the useEffect callback we pass into the first argument, we can run code when the state in the 2nd argument changes since the callback in the first argument will be run when the state in the 2nd argument changes.

For instance, we can write:

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

export default function App() {
  const [loading, setLoading] = useState(false);
  const [loading2, setLoading2] = useState(false);

  useEffect(() => {
    setTimeout(() => setLoading(true), 1000);
  }, []);

  useEffect(() => {
    if (loading) {
      setTimeout(() => setLoading2(true), 1000);
    }
  }, [loading]);

  return (
    <>
      <div>{loading.toString()}</div>
      <div>{loading2.toString()}</div>
    </>
  );
}

We have 2 states, loading and loading2 , which we updated inside the useEffect callbacks.

In the first useEffect callback, we call setTimeout with a callback that calls setLoading to set loading to true after 1000 milliseconds.

The empty array in the 2nd argument means that the useEffect callback only runs when the component is being mounted.

Next, we call useEffect again with a callback that gets the loading value.

In the callback, we check if loading is true ,

If it is, then we call setTimeout with a callback to call setLoading2 to set loading2 to true after 1000 milliseconds.

In the 2nd argument, we have an array that has the loading state.

This means whenever the loading state changes, the useEffect callback will run.

This means useEffect lets us run code on state updates.

In the JSX, we render the values of each state.

And we should see them become true one after the other.

Conclusion

We can run async code after state changes by calling the useEffect hook to watch the value of a state by passing the states we want to watch into the array in the 2nd argument of useEffect .

Then we can run code according to the value changes in the useEffect callback.

Categories
React

How to Clear Timeout and Interval Timers with React Hooks?

Timers created with setTimeout or setInterval are used often in React apps.

setTimeout lets us run code after a delay.

And setInterval lets us run code periodically.

To free up resources and to stop the timers from running, we should call clearTimeout to stop any timers created with setTimeout from running.

Likewise, we should call clearInterval to stop any timers created with setInterval from running.

In this article, we’ll look at the right way to clear timers created with these functions in React components created with hooks.

Clear Timers Created with setTimeout

To clear timers that are created with setTimeout , we should call clearTimeout in the useEffect callback is the function that’s run when the component unmounts.

The function that we return in the useEffect callback is run when the component unmounts, so we should put it there,.

For instance, we can write:

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

export default function App() {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const timer = setTimeout(() => setLoading(false), 1000);

    return () => {
      clearTimeout(timer);
    };
  }, []);

  return <div>{loading ? "loading" : "hello"}</div>;
}

We have the loading state which is initially set to true .

Then we call useEffect with a callback.

In the callback, we call setTimeout with a callback that calls setLoading with false to set loading to false .

And the callback is run after 1000 milliseconds since the 2nd argument is 1000.

Also, we return a function that calls clearTimeout with timer to clear the timer.

This function is run when we unmount the component.

Then we display text according to the value of loading .

So we should see ‘loading’ first. Then we should see ‘hello’ about 1 second later.

Clear Timers Created with setInterval

Likewise, we can clear a timer created with setInterval the same way.

For instance, we can write:

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

export default function App() {
  const [time, setTime] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => setTime((t) => t + 1), 1000);

    return () => {
      clearInterval(timer);
    };
  }, []);

  return <div>{time}</div>;
}

Almost everything is the same except we replaced setTimeout and clearTimeout with setInterval and clearInterval .

Timers Outside the useEffect Callback

We cal also place timers outside the useEffect callback.

For instance, we can write:

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

export default function App() {
  const [time, setTime] = useState(0);
  const timer = useRef();

  useEffect(() => {
    timer.current = setInterval(() => setTime((t) => t + 1), 1000);
  }, []);

  useEffect(() => {
    return () => {
      clearInterval(timer.current);
    };
  }, []);

  return <div>{time}</div>;
}

We create the timer ref with the useRef hook.

Then we set timer.current to the timer returned by setInterval .

Then we can call clearInterval anywhere in our component code.

The same also applies to setTimeout and clearTimeout .

Conclusion

We can store our timers in a ref or a variable in the useEffect callback so we can clear them when we no longer need them.