The useRef hook is a multi-purpose hook. Its primary purpose in Melody is to store and update values without causing a re-evaluation of the state hook. Thus, you might say it offers a non-reactive value storage. Coming from React, it has inherited the name useRef and the additional capability to reference DOM elements. However, as mentioned in , Melody offers better ways to bind DOM elements and you should not rely on useRef for that purpose.
The term "non-reactive" refers to a value that, when changed, does not cause the component to be re-evaluated. Values produced by useState or useAtom will cause re-evaluation whenever they are modified through the mutator function.
Storing non-reactive values
// a small hook to contain interval logic which can be canceled
function usePulse({ interval, callback }) {
// create a ref
const timer = useRef(null);
// timer.current will contain our value over time and will always
// refer to the latest value
const cancel = useCallback(() => clearInterval(timer.current));
useEffect(() => {
// update the value with the new interval
timer.current = setInterval(callback, interval);
return cancel;
}, [interval, callback]);
// and return a function to cancel it
return cancel;
}
function usePulseButton() {
const [shouldPulseIn, setPulseIn, isPulseIn] = useAtom(true);
// on each interval tick, we'll flip the pulse value
// that'd then trigger a CSS class change to do an animation
const cancelPulse = usePulse(1000, useCallback(() => {
setPulseIn(!shouldPulseIn());
}));
const buttonRef = useCallback(element => {
return fromEvent(element, 'click').subscribe(event => {
// when the value is clicked we'll stop the animation
// using the callback
cancelPulse();
});
});
return {
buttonRef,
isPulseIn
};
}
Storing all referenced elements
function useReferencedElements() {
// create a non-reactive variable which will hold all of our elements
const elements = useRef([]);
// use a normal ref handler to collect all referenced elements
const refElement = useCallback(element => {
elements.current.push(element);
// and make sure we drop them from the list if anything changes
return () => removeElement(elements.current, element);
}, []);
// provide easy access to the refHandler and the array of elements
return [refElement, elements.current];
}
function useItemList({ data }) {
const [item, itemElements] = useReferencedElements();
// not a particularly good example but this would animate all referenced
// item elements and would replay the animation if any of them changed
useEffect(() => {
itemElements.forEach(element => element.animate(...));
}, itemElements);
return {
item,
data
};
}
// utility function to remove an element from an array
const removeElement = (array, element) => {
const index = array.indexOf(element);
if (index < 0) return;
array.splice(index, 1);
};
Easy access to just a single referenced element
function useForm({ submitForm }) {
// the return value of useRef can be assigned to an element
// using the ref attribute
// its just a special ref handler
const inputElement = useRef(null);
// Let's also discuss how to reference an element for reading while
// binding event handlers
const buttonElement = useRef(null);
// but for event handling we really want to leverage the
// ref handler directly
const submitButton = useCallback(element => {
// just store the current element
buttonElement.current = element;
return fromEvent(element, 'click').subscribe(() => {
submitForm({ text: inputElement.value });
}).add(
// Rx allows to add teardown logic to an existing subscription
() => buttonElement.current = null
);
}, [submitForm]);
return {
inputElement,
submitButton
};
}