Categories
React

How to Fix the React useState Hook Not Setting Initial Value Problem?

The useState hook lets us create state variables in our React components.

It takes an argument for the initial value of the state.

Sometimes, we may want to set the initial value of a state from props.

And we want to update the initial value when the prop value changes.

In this article, we’ll look at how to fix the React useState hook with the latest prop value.

Updating a State When a Prop Updates

To update a state when a prop updates, we’ve to watch the prop value with the useEffect hook.

Then in the useEffect callback, we can call the state setter function to update the state value with the prop’s value.

For instance, we can write:

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

const Count = ({ count }) => {
  const [num, setNum] = useState(count);

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

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

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

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

In the Count component, we have the useState hook with the count value as the argument.

This sets num to count initially.

Then we have the useEffect hook that watches the count value by passing it into the array in the 2nd argument.

Then in the callback, we call setNum to update the num value and render that in the return statement below that.

In App , we have the count state created with the useState hook.

Then we call setCount in the onClick handler of the button which updates the value of the count state.

And we pass the count value as the value of the count prop in the Count component.

Now when we click on the increment button, we see the num value update and the latest value of it displayed.

Conclusion

We can make sure that a React component state updates when the prop value changes by watching the prop’s value with the useEffect hook and then call the state setter function in the useEffect callback with the prop’s value as its argument.

Categories
React Answers

How to Use the React useEffect Hook with Debounce?

Sometimes, we may not want to run the code in the useEffect hook immediately after a state update.

In this article, we’ll look at how to use the useEffect hook with the code inside the useEffect callback debounced.

Use the React useEffect Hook with Debounce

We can create our own hook that uses the useEffect hook to run code with the useEffect callback debounced.

To do this, we can write:

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

const useDebounce = (value, delay) => {
  const [debouncedValue, setDebouncedValue] = useState("");
  const firstDebounce = useRef(true);

  useEffect(() => {
    if (value && firstDebounce.current) {
      setDebouncedValue(value);
      firstDebounce.current = false;
      return;
    }

    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

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

return debouncedValue;
};

export default function App() {
  const value = useDebounce("abc", 1000);

  useEffect(() => {
    console.log(value);
  }, [value]);

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

We create the useDebounce hook with the value and delay parameters.

value is the value we want to set.

delay is the denounce delay for the useEffect callback code.

We have the firstDebounce ref to keep track of whether the denounced code is running the first time.

In the useEffect callback, we set firstDebounce.current to false so that we know that it’s not the first time that the denounced code is run it.

Then we call the setTimeout function with a callback with the denounced code.

In the callback, we call setDebouncedValue to set the debouncedValue state value.

Then we return the call that runs clearTimeout which runs when the component is unmounted.

In App , we call useDebounce with the value we want to set and the delay.

Then we log the value in the useEffect callback when the value value changes.

And we also render the value below that.

Now we should see the 'abc' string rendered and logged after 1 second.

Conclusion

We can create our own hook to run code that we want to denounce within the useEffect callback.

Categories
React

How to Update a State Inside the setInterval Callback in a React Hook?

The setInterval function lets us run code in our JavaScript apps periodically.

We may sometimes use this in React component to update a state periodically with new values.

In this article, we’ll look at how to update a state inside the setInterval callback in a React hook.

Running setInterval in a React Component

To run setInterval in a React component, we should put it in the useEffect hook.

The useEffect hook is used for committing side effects, which includes creating timers.

Therefore, we can write something like:

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

export default function App() {
  const [gamePlayTime, setGamePlayTime] = useState(0);

  useEffect(() => {
    const gameStartInternal = setInterval(() => {
      setGamePlayTime((t) => t + 1);
    }, 1000);

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

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

We have the gamePlayTime state which we update by calling setGamePlayTime .

Next, we call useEffect with a callback and create the timer inside the callback by calling setInterval .

We pass in 1000 as the 2nd argument so that the setInterval callback only runs 1000 milliseconds.

It returns a timer ID so that we can call clearInterval on it when the component unmounts.

And we did that in the callback we return in the useEffect callback.

The callback we return is run when we unmount the component.

We pass in an empty array into the 2nd argument so that the useEffect callback runs only once when the component mounts.

This way, we don’t create the timer more than once.

Then below that, we display the gamePlayTime .

Now we should see the gamePlayTime value update every second on the screen and the latest value displayed.

Conclusion

We just call setInterval in the useEffect callback to create the timer.

When we unmount the component, we call clearInterval in the callback we return to clear it when we unmount the component.

Categories
React

How to Run the React useEffect Hook Callback Only When All Dependencies Change?

Sometimes, we only want to run the useEffect hook callback when all the dependencies change.

In this article, we’ll look at how to run the React useEffect hook callback only when all the dependencies change.

React useEffect Hook Callback Only When All Dependencies Change

To run the useEffect hook callback only when all the dependencies change, we can store the previous values in a ref and then compare them to the latest values.

If the previous values are different from the previous values, then we run the useEffect callback code.

For instance, we can write:

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

export default function App() {
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  const previousValues = useRef({ name, age });

  useEffect(() => {
    if (
      previousValues.current.name !== name &&
      previousValues.current.age !== age
    ) {
      console.log(name, age);
      console.log(previousValues.current);
      previousValues.current = { name, age };
    }
  });

  return (
    <div>
      <input
        placeholder="name"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <input
        placeholder="age"
        value={age}
        onChange={(e) => setAge(e.target.value)}
      />
    </div>
  );
}

We have the name and age states that we defined with the useState hook.

Then we create the previousValues ref to store the previous values of name and age in an object.

We store them in a ref so that when the value of it changes, it won’t trigger a re-rendering of the component.

This lets us get both the previous and current values since re-rendering will erase the previous state values if we don’t store them in a ref.

Then in the useEffect callback, we compare the values of the current and previous values of name and age .

And if they’re both different, then we log the values of name and age .

And then we store the previous values of the name and age in an object and assign them to previousValues.current to store them in a ref.

We don’t pass in a 2nd argument to useEffect so it runs on every render.

Below that, we have 2 inputs that we can type into to change the value of name and age .

Now when we type in different values into the inputs, we see the console log run.

Conclusion

To run code in a useEffect callback when all the dependencies change, we can store the previous values of the states in a ref.

Then we can compare them in the useEffect callback and all the previous and current values are different, then we can run the code we want to run.

Categories
React

How to Use React Context API with Multiple Values for Providers?

The React Context API is handy for letting us share data between components easily.

Sometimes, we may want to share multiple states between components.

In this article, we’ll look at how to share multiple states between components with one context provider.

Use React Context API with Multiple Values for Providers

We can pass in anything we want to the value prop of the context provider component.

So sharing multiple states with one provider is no problem.

For instance, we can write:

import React, { useContext, useState } from "react";
const CountContext = React.createContext("count");

const DescendantA = () => {
  const { count, setCount, count2, setCount2 } = useContext(CountContext);

  return (
    <>
      <button onClick={() => setCount((c) => c + 1)}>Click me {count}</button>
      <button onClick={() => setCount2((c) => c + 1)}>Click me {count2}</button>
    </>
  );
};
const DescendantB = () => {
  const { count, setCount, count2, setCount2 } = useContext(CountContext);

return (
    <>
      <button onClick={() => setCount((c) => c + 1)}>Click me {count}</button>
      <button onClick={() => setCount2((c) => c + 1)}>Click me {count2}</button>
    </>
  );
};
export default function App() {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);
  return (
    <CountContext.Provider value={{ count, setCount, count2, setCount2 }}>
      <DescendantA />
      <DescendantB />
    </CountContext.Provider>
  );
}

We create the CountContext with the React.createContext method.

Then we wrap the CountContext.Provider component around the DescendantA and DescendantB components.

We pass in the state getter and setters we defined in App all to one object into the value prop.

Then in the DescendantA and DescendantB components, we call useConext with CountContext to return the object we passed into the value prop.

We destructured all the properties and use them.

We show count and count2 in the buttons.

And we call setCount and setCount2 in the button click handlers.

Now when we click on the buttons, we see the counts go up.

Conclusion

We can pass in anything we want into the value prop of the context provider component in React.

So we can share anything between components as long as we pass them into the value prop.