Skip to content

Instantly share code, notes, and snippets.

@Kingdutch
Created March 15, 2019 15:07
Show Gist options
  • Select an option

  • Save Kingdutch/745847a91b1e646ab6fe0be41b1d3c04 to your computer and use it in GitHub Desktop.

Select an option

Save Kingdutch/745847a91b1e646ab6fe0be41b1d3c04 to your computer and use it in GitHub Desktop.
A custom hook to update state using an interval and trigger a callback after a certain time.
import { useReducer, useCallback, useState } from "react";
import useInterval from "./useInterval";
const initialState = {
brewing: false,
tea: null,
brewTime: 0,
brewStart: 0,
elapsed: 0
};
const brewReducer = (state, action) => {
switch (action.type) {
case "start":
return {
...initialState,
brewing: true,
tea: action.tea,
brewTime: action.brewTime,
brewStart: new Date()
};
case "tick":
return {
...state,
elapsed: (action.time - state.brewStart) / 1000
};
case "stop":
return {
...state,
elapsed: state.brewTime,
brewing: false
};
case "reset":
return initialState;
default:
throw new Error();
}
};
/**
* Manages the brewing of a cup of tea.
*
* Call setTeaBrewing with a tea id and brew time to start the brew timer for a
* cup of tea. Once brewing is complete the brewCompleteCallback will be called
* with the tea id and brew time.
*
* @param {Function?} brewCompleteCallback
* Called when the brewTime has elapsed with the tea id and brew time.
*
* @return {[Object, Function]}
* An array containing the brewState and the setTeaBrewing callback.
*/
const useBrewManager = brewCompleteCallback => {
const [callbackCalled, setCallbackCalled] = useState(false);
const [brewState, dispatch] = useReducer(brewReducer, initialState);
const setTeaBrewing = useCallback(
(tea, brewTime) => {
setCallbackCalled(false);
dispatch({ type: "start", tea, brewTime });
},
[dispatch, setCallbackCalled]
);
const tickInterval = useCallback(() => {
const now = new Date();
const elapsed = (now - brewState.brewStart) / 1000;
// If the brewTime has not elapsed yet then we simply update our state.
if (elapsed < brewState.brewTime) {
dispatch({ type: "tick", time: now });
}
// Otherwise we trigger the brew complete callback and stop brewing.
else {
dispatch({ type: "stop" });
if (brewCompleteCallback && !callbackCalled) {
setCallbackCalled(true);
brewCompleteCallback(brewState.tea, brewState.brewTime);
}
}
}, [
dispatch,
brewState.brewStart,
brewState.brewTime,
brewState.tea,
brewCompleteCallback,
callbackCalled,
setCallbackCalled
]);
useInterval(tickInterval, brewState.brewing ? 500 : null);
return [brewState, setTeaBrewing];
};
export default useBrewManager;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment