# Passing ref by useContext ## What is ref in React? **Ref is a plain JavaScript object with the current property that you can read and modify.** Ref is designed to step outside React since it won't trigger re-render, but situations like 1. [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) 2. Storing and manipulating DOM elements like [scrollIntoView API]([https:/](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView)/) **which are some value and need to be stored somewhere.** (They don’t impact the rendering logic like state, so we choose refs.) ## How to pass a ref? Normally, we use [forwardRef()](https://react.dev/reference/react/forwardRef) to pass a ref to the parent component, /Input.ts(children component) ```typescript import { forwardRef } from 'react'; const ChildrenInput = forwardRef(function Input(props, ref) { const { label, ...otherProps } = props; return ( <label> {label} <input {...otherProps} ref={ref} /> </label> ); }); export default Input; ``` /App.ts(parent component) ```typescript import { useRef } from 'react'; import ChildrenInput from './ChildrenInput.ts'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> // parent component <ChildrenInput label="Enter your name:" ref={ref} /> <button type="button" onClick={handleClick}> Click here to focus the ref input </button> </form> ); } ``` ## Passing a ref in a different way However, when you need to develop a new feature in a large project, you may face a problem that passing a ref into other component(not the parent component) like this ```typescript <Anchor/> // there are dozens of components here <Card/> ``` You are not able to pass a ref inside <Card> to <Anchor>. To solve this problem, we can use useContext to wrap them up: ```typescript import React, { RefObject, useState } from "react"; import Card from "./Card"; import Anchor from "./Anchor"; interface IContext { dispatchRef: (ref: RefObject<HTMLDivElement>) => void; scrollToRef: () => void; } export const StoreContext = React.createContext<IContext>({ dispatchRef: () => {}, scrollToRef: () => {}, }); const RefProvider = () => { const [ref, setRef] = useState<RefObject<HTMLDivElement> | null>(null); /** * set the ref state * @param ref Element */ const dispatchRef = (ref: RefObject<HTMLDivElement>) => { setRef(ref); }; /** * scroll to the ref */ const scrollToRef = () => { if (ref) { ref.current?.scrollIntoView({ behavior: "smooth" }); } }; return ( <StoreContext.Provider value={{ dispatchRef, scrollToRef }}> <Anchor /> <Card /> </StoreContext.Provider> ); }; export default RefProvider; ``` ```typescript import { useContext, useRef } from "react"; import { StoreContext } from "./RefProvider"; const Card = () => { const { dispatchRef } = useContext(StoreContext); const cardRef = useRef<HTMLDivElement>(null); dispatchRef(cardRef); return ( <div className="card" ref={cardRef}> Card </div> ); }; export default Card; ``` ```typescript import { useContext } from "react"; import { StoreContext } from "./RefProvider"; const Anchor = () => { const { scrollToRef } = useContext(StoreContext); return ( <div className="anchor" onClick={scrollToRef}> Click me to scroll to Card </div> ); }; export default Anchor; ``` ## Conclusion - This is just a concept of passing ref in a different way instead of forwardRef - Please consider the structure of your project before you decide to pass a ref by useContext - There are trade-offs like adding a new Provider is a must since it's useContext [Demo of passing ref by useContext ](https://codesandbox.io/p/sandbox/passingrefdemo-s5rr4l?file=%2Fsrc%2FApp.css%3A23%2C15)