Jennifer
Jennifer

Reputation: 633

Using ref react hooks multiple times

I'm trying to use ref API many times with setInterval method but only one is working

I have created a ref as an array then I'm trying to insert the function to the array by using the index key but that works only for the first insert I don't know what I'm doing wrong

here's what I've achieved

import React, { useState, useEffect, useRef, createRef } from "react";
import ReactDOM from "react-dom";

function Counter() {
  const [countSec, setCountSec] = useState(0);
  const [countMin, setCountMin] = useState(0);

  useInterval(() => {
    setCountSec(countSec + 1); // this working 
  }, 1000, 0);

  useInterval(() => {
    setCountMin(countMin + 1); // it's not working
  }, 1100, 1);

  return <div>
      <h1>{countSec} Secounds</h1>
      <h1>{countMin} Half-Minutes</h1>
  </div>;
}

function useInterval(callback, delay,index){
  const savedCallback = useRef([...Array(2)].map(()=> createRef())); 
  // Remember the latest function.
  useEffect(() => {
      savedCallback.current[index].current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
      let id = setInterval(()=>savedCallback.current[index].current(), delay);
      return () => clearInterval(id);
  });
};

https://codesandbox.io/s/sharp-tree-k39ts

Upvotes: 2

Views: 2502

Answers (2)

Lu&#239;s
Lu&#239;s

Reputation: 2833

There is no need to try to remember multiple callbacks in your useRef. Everytime you use useInterval it creates a new instance of your hook. So they will work independently from eachother. This means the first time you use useInterval it will create a new useRef for your setCountSec(countSec + 1) callback. And the second time you use useInterval it will create another instance of useRef for your setCountMin(countMin + 1). All you have to do it change the interval to have the second one update once every 30 seconds.

import React, { useState, useEffect, useRef, createRef } from "react";
import ReactDOM from "react-dom";

function Counter() {
  const [countSec, setCountSec] = useState(0);
  const [countMin, setCountMin] = useState(0);

  useInterval(() => {
    setCountSec(countSec + 1);
  }, 1000); // update every 1000ms (1sec)

  useInterval(() => {
    setCountMin(countMin + 1);
  }, 30000); // update every 30000ms (30sec)

  return <div>
      <h1>{countSec} Secounds</h1>
      <h1>{countMin} Half-Minutes</h1>
  </div>;
}

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest function.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

Upvotes: 2

ChandraKumar
ChandraKumar

Reputation: 537

Based on your comment

you need to update countMin after countSec

Here is an working code

function Counter() {
  const [countSec, setCountSec] = useState(0);
  const [countMin, setCountMin] = useState(0);

  useEffect(()=>{
    setInterval(() => {
     setCountSec(countSec + 1); // this working 
   }, 1000);

   setInterval(() => {
    setCountMin(countMin + 1); // it's not working
   }, 1100);
  }, [])


  return <div>
      <h1>{countSec} Secounds</h1>
      <h1>{countMin} Half-Minutes</h1>
  </div>;
}

few pointers on your code would be for every rerender the whole function would get called and a new setInterval would be created...

useEffect(()=>{},[]) is equivalent to componentDidMount

if you want to follow your patten without this useEffect(()=>{},[]) you could use setTimeout

Upvotes: 0

Related Questions