"Instead of passing a ref attribute created by useRef(), you pass a function. The function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere." React Docs
1import { useRef, useState } from "react";23export default function App() {4 const myRef = useRef({});5 const [fruits, setFruits] = useState(["apple", "pear", "pineapple"]);67 const onClick = (fruit) => {8 // console.log(myRef)9 console.log(myRef.current[fruit]);10 setFruits((s) => [...s, Math.random().toString().slice(0, 4)]);11 // adding something to 'fruits', showing that this works with dynamic data12 };1314 return (15 <>16 {fruits.map((f) => (17 <button18 // passing a function that recieves the DOM node (ref) of each item19 // & stores it on the myRef.current object20 ref={(ref) => (myRef.current[f] = ref)}21 //22 onClick={() => onClick(f)}23 key={f}24 >25 {f}26 </button>27 ))}28 </>29 );30}CodeSandbox
1import { useRef, useState } from "react";23export default function App() {4 const myRef = useRef({});5 const [todos, setTodos] = useState([6 "todo 1",7 "todo 2",8 "todo 3",9 "todo 4",10 "todo 5",11 "todo 6",12 "todo 7",13 "todo 8",14 "todo 9",15 "todo 10",16 "todo 11",17 "todo 12",18 "todo 13",19 "todo 14",20 "todo 15",21 ]);2223 const onClickTop = () => {24 myRef.current[todos[0]].scrollIntoView();25 };2627 const onClickBottom = () => {28 myRef.current[todos[todos.length - 1]].scrollIntoView();29 };3031 const onClickNewTodo = () => {32 setTodos((s) => [...s, "todo " + Math.random().toString().slice(0, 5)]);33 };3435 return (36 <>37 <button onClick={onClickTop}>Scroll to Top</button>38 <div39 style={{40 overflow: "auto",41 height: "100px",42 width: "200px",43 border: "1px solid black",44 }}45 //scrollable container46 >47 {todos.map((f) => (48 //same as before49 <li ref={(ref) => (myRef.current[f] = ref)} key={f}>50 {f}51 </li>52 ))}53 </div>5455 <button onClick={onClickBottom}>Scroll to bottom</button>56 <button onClick={onClickNewTodo}>Add New Todo</button>57 </>58 );59}CodeSandbox