setting focus on one of the textarea out of many

Viewed 31
{arr.map((i) => { // arr = [1,2,3,4,5], taRef=useRef()
    return (
      <div key={i}>
        <textarea ref={taRef} placeholder="Enter comment" rows={5} />
        <button onClick={setFocus}>Set Focus</button>
      </div>
    );
  })}

I want to set focus on the corresponding textarea element when ever I click on the corresponding button. Right now it is always setting the focus on the last element.

Check the sandbox example here

3 Answers

That is because you're initialising the reference which then get reassigned every time you iterate through the next object.

In order to fix this, setup your reference as followed:

// Before your render method and instead of your `useRef`
const refs = []

// Inside your render
{arr.map((i) => {
    const ref = createRef()
    refs.push(ref)

    return (
      <div key={i}>
        <textarea ref={ref} placeholder="Enter comment" rows={5} />
        <button onClick={setFocus}>Set Focus</button>
      </div>
    );
  })}

So instead of reassigning the same ref every time we iterate the new object, we create a new one for each textarea and then push it to the refs array.

You will then be able to access all individual references by:

console.log(refs[0].current)

You are writing into ref only one integer and expecting to remember five different pieces of information? Well, hard to achieve.

Anyways, assuming that you have a stable HTML structure, you use the following implementation

export default function App() {
  const arr = [1, 2, 3, 4, 5];
  const setFocus = (event) => {
    const ta = event.target.previousElementSibling; #2 Those unique references allow you to pick that previous element which is text area and do whatever you want with them.
    ta.focus();
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      {arr.map((i) => {
        return (
          <div key={i}>
            <textarea placeholder="Enter comment" rows={5} />
            <button onClick={(e) => setFocus(e)}>Set Focus</button>  // #1 every onClick uses a 'unique' reference.
          </div>
        );
      })}
    </div>
  );
}


You are passing the same ref to every element, so when you try to use it, it's current the last one. So you should get new refs for every one of your elements. Try this.

export default function App() {
  const arr = [1, 2, 3, 4, 5];
  const getRef = () => {
    return new useRef();
  };
  const setFocus = (taRef) => () => {
    if (taRef.current) taRef.current.focus();
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      {arr.map((i) => {
        const taRef = getRef();
        return (
          <div key={i}>
            <textarea ref={taRef} placeholder="Enter comment" rows={5} />
            <button onClick={setFocus(taRef)}>Set Focus</button>
          </div>
        );
      })}
    </div>
  );
}
Related