I am trying to consume data from context, into a custom hook. the problem is that the data is not ready and the hook takes the default values.
How can I make my custom hook take the updated values once the context is ready?
timer context
the context expects two values that I get from the database
//context
export const TimerContext = createContext();
//hook
export const useTimer = () => useContext(TimerContext);
//provider
export const TimerProvider = ({ children }) => {
//estado del context
const [timer, setTimer] = useState({
inicioPartido: "Apr 8, 2022 01:00:00",
finPartido: "Apr 8, 2022 01:10:00",
});
return (
<TimerContext.Provider value={{ timer, setTimer }}>
{children}
</TimerContext.Provider>
);
};
page index.js
timers it is an object that I get from the database, here everything works fine
the object exists and is stored in the context
const {timer, setTimer } = useTimer();
useEffect(() => {
setTimer({
inicioPartido: timers.start_timer,
finPartido: timers.end_timer,
});
}, [timers]);
the issue
when I pass both dates to my custom hook, it gets only the default values that I put in the context
const { inicio, termino, segundo, minuto, hora, dia } = useCounterhook(
timer.inicioPartido,
timer.finPartido
);
instead
If I send the dates hard coded everything works perfectly
const { inicio, termino, segundo, minuto, hora, dia } = useCounterhook(
"Apr 8, 2022 03:40:00",
"Apr 8, 2022 03:50:00"
);
MY CUSTOM HOOK
my hook compares the distance between two dates and generates a countdown
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
const useCounterhook = (fechaInicio, fechaTermino) => {
//states
const [segundo, setSegundo] = useState(0);
const [minuto, setMinuto] = useState(0);
const [hora, setHora] = useState(0);
const [dia, setDia] = useState(0);
const [inicio, setInicio] = useState(false);
const [termino, setTermino] = useState(false);
//real time settings
const second = 1000;
const minute = second * 60;
const hour = minute * 60;
const day = hour * 24;
useEffect(() => {
setInterval(() => {
let inicio = new Date(fechaInicio).getTime();
let termino = new Date(fechaTermino).getTime();
let ahora = new Date().getTime();
let distance = inicio - ahora;
if (inicio > ahora && termino > ahora) {
//si fechaInicio es mayor a este instante
//partido aun no inicia
setDia(Math.floor(distance / day));
setHora(Math.floor((distance % day) / hour));
setMinuto(Math.floor((distance % hour) / minute));
setSegundo(Math.floor((distance % minute) / second));
setInicio(false);
setTermino(false);
}
if (inicio < ahora && termino > ahora) {
//si fecha inicio es menor a este instante
// partido ya inicio y aun no termina
setDia(0);
setHora(0);
setMinuto(0);
setSegundo(0);
setInicio(true);
setTermino(false);
}
if (inicio < ahora && termino < ahora) {
//si fecha inicio es mayor a fecha de termino
//el partido ya termino
setDia(0);
setHora(0);
setMinuto(0);
setSegundo(0);
setInicio(false);
setTermino(true);
}
}, 1000);
}, []);
return { inicio, termino, segundo, minuto, hora, dia };
};
export default useCounterhook;
useCounterhook.propTypes = {
fechaInicio: PropTypes.string,
fechaTermino: PropTypes.string,
};
I think it is because the context is not ready, when the hook is already waiting for the values, but I don’t know how to solve it
>Solution :
The problem here is that the useCounterHook is running the useEffect based on the first time it is mounted and then never accounts for when the timer values change.
If you make use of the useEffect dependency array you can resolve this.
Update the useEffect in your useCounterHook to the following and you should be good to go:
useEffect(() => {
const interval = setInterval(() => {
// this content doesn't change
}, 1000);
// useEffect's cleanup method
return () => {
clearInterval(interval)
}
// dependency array to listen if `fetchaInicio` or `fetchTermino` changes
}, [fechaInicio, fechaTermino]);
Now your hook will check if either of those inputs change and if that is the case the timer will stop and then start a new timer with the new values.