useRef hook
How to use the useRef hook to keep a value of a variable stable across renders.
Description
The useRef hook will save the value of a variable across renders.
Consider the following code:
function Component() {
const [changed, setChanged] = useState(false);
let isChanged = 'State did not change';
const performChange = () => {
isChanged = 'State changed!';
setChanged(true);
};
return html`
${isChanged}
<button @click=${performChange}>Change me!</button>
`;
}The above code will not work. But why?
This will be the component's lifecycle:
- The component is in the DOM, so it will try to perform its first render and the
Component()function will be executed. isChangedis set to "State did not change"- The component is fully rendered
- The user clicks the button
isChangedis set to "State changed!" and thechangedstateful variable is set to true.- The new state differs from the previous state: the component is reloaded and the
Component()function is executed. - Again,
isChangedis set to "State did not change" - The component is fully rendered
Usually, you never want to make "normal" variable declarations inside of your component if you plan to change the variable's value at some point of the component's lifecycle.
You may think: "What if I move the variable declaration outside of the component?".
This approach would actually work, but you don't want to do it, for two reasons:
- Every instance of the component will have the same value: they are not independent.
- The previous reason implies that the component is NOT Pure, and this can lead to unexpected behaviors.
If you plan to use the component only once though, feel free do to it (but it'll make us sad).
The useRef hook will solve this problem.
Warning: Seeing a "let" or "var" variable declaration inside of your component should always trigger some alarms. The only place you should use " let" or "var" variables instead of "const" variables is (maybe) inside other functions (events, etc.).
The useRef hook has also a second use (which is usually the most common): if you put the value returned by it in a "ref" attribute of any node, the value of the variable will become the actual node.
Usage
const ref = useRef(initialValue);The useRef hook accepts a single parameter, the initial value, and will return an object having a "current" key, which will correspond to the current value of the variable.
To update the value of the variable, you have to update the value of the "current" key.
Note: unlike the
As said in the Description chapter, you can also use the value returned by the hook as the value of a "ref" attribute of any node. The value will be assigned after the first render (not immediately).
Quick Example:
function Component() {
const nodeRef = useRef();
console.log(nodeRef.current); // ❌ On the first render it will be null!
useEffect(() => {
console.log(nodeRef.current); // ✅ It will already be valorized at this point!
console.log(nodeRef.current.textContent); // "Hey!"
}, []);
return html` <div ref=${nodeRef}>Hey!</div> `;
}Example: timer
In this example we will create a simple timer using the useRef hook to save the value of the intervalId once we start the timer.
import { useState, defineWompo, html, useRef } from 'wompo';
export default function Timer() {
const [timer, setTimer] = useState(0);
const intervalId = useRef(null);
function startTimer() {
intervalId.current = setInterval(() => {
setTimer((oldTimer) => oldTimer + 1);
}, 10);
}
function stopTimer() {
clearInterval(intervalId.current);
intervalId.current = null;
}
function resetTimer() {
setTimer(0);
}
return html`
<div>
<button @click=${startTimer} disabled=${intervalId.current !== null}>Start</button>
<button @click=${stopTimer} disabled=${intervalId.current === null}>Stop</button>
<button @click=${resetTimer} disabled=${timer === 0}>Reset</button>
<p>${(timer / 100).toFixed(2)}</p>
</div>
`;
}
defineWompo(Timer);Result:
Example: password revealer
In this example we will get the reference of an input node using the useRef hook and display an alert showing it's value.
import { defineWompo, html, useRef } from 'wompo';
export default function PasswordRevealer() {
const inputRef = useRef(null);
const revealPassword = () => {
alert(`Your password is: "${inputRef.current.value}" 😈`);
};
return html`
<div>
<label>
Type your password here:
<input ref=${inputRef} type="password" />
<button @click=${revealPassword}>I'll show your password to everyone!</button>
</label>
</div>
`;
}
defineWompo(PasswordRevealer);Result:
useReducer - Wompo hooks
The useReducer hook will let manage the state of your component in a Redux-like approach.
useSelf - Wompo hooks
The useSelf hook will return the HTML instance of the custom component.