React – How to use Hooks (2)?

useRef

How to persist values between renders?

Use useref

/*
If we tried to count how many times our application renders using the useState Hook, we would be caught in an infinite loop since this Hook itself causes a re-render.

To avoid this, we can use the useRef Hook.
*/

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

function App() {
  const [inputValue, setInputValue] = useState("");
  const count = useRef(0);

  useEffect(() => {
    count.current = count.current + 1;
  });

  return (
    <>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <h1>Render Count: {count.current}</h1>
    </>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

How to store an array of elements using useRef?

Problem: I could not simply assign my ref to the element because the ref was being assigned within a map during the render. This would mean that the very last item in the array would be the element assigned to item.current

Solution:

const itemEls = useRef({})

{items.map((item, index)) => (
 <p key={item} ref={(element) => itemEls.current[index] = element}>{item}</p>
))
  const itemsEls = useRef(new Array())
  
  return (
    {items.map((item) => {
      const getRef = (element) => (itemsEls.current.push(element))
      return <p key={getRef}>{item}</p>
    })}
  )

How to access DOM elements?

Use useref

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

How to keep track of previous state values?

Use react_useref

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

function App() {
  const [inputValue, setInputValue] = useState("");
  const previousInputValue = useRef("");

  useEffect(() => {
    previousInputValue.current = inputValue;
  }, [inputValue]);

  return (
    <>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <h2>Current Value: {inputValue}</h2>
      <h2>Previous Value: {previousInputValue.current}</h2>
    </>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

forwardRef

How to pass ref into our custom components?

  • Create custom component with forwardRef
function Button(props, ref) {
  return (
    <button ref={ref}>
      {props.children}
    </button>
  );
}

export default forwardRef(Button);
  • Pass ref from parent component to child component
const ref = React.createRef();
<Button ref={ref}>Click me!</Button>;

useImperativeHandle

How to expose limited properties on a ref?

  • App.tsx
function App() {
  const buttonRef = useRef(null);

  const handleClick = () => {
    console.log(Object.keys(buttonRef.current)); // ['someExposedProperty']
    console.log("click in index.tsx");
    buttonRef.current.someExposedProperty();
  };

  return (
    <div>
      <Button onClick={handleClick} ref={buttonRef} />
    </div>
  );
}
  • Button.tsx
import React, { useRef, useImperativeHandle, forwardRef } from "react";

function Button(props, ref) {
  const buttonRef = useRef(); // To use useImperativeHandle you need to work with another ref instance

  useImperativeHandle(ref, () => ({
    someExposedProperty: () => {
      console.log(`we're inside the exposed property function!`);
      // Do something with "buttonRef.current", for example "buttonRef.current.blur()"
    }
  }));
  return (
    <button ref={buttonRef} {...props}>
      Button
    </button>
  );
}

export default forwardRef(Button);

Be the first to comment

Leave a Reply

Your email address will not be published.


*