catandmouse
catandmouse

Reputation: 11829

How to autofocus on a specific input field in React (after clicking on another element)?

I have several input fields in a table (each row has an input field) and each of them are named dynamically like so:

<table>
  <tr>
    <td>
     <input
      key={name}
      type="text"
      value={label}
      name={name}
     />

     <span>Edit</span>
   </td>
  </tr>
</table>

Note I am mapping on some object to render each <td /> that contains an input field.

What I'd like to happen is when I click on "Edit" for that particular row, the input field on the same row will become focused.

What I am seeing from React docs is how to trigger this focus if you have a predefined set of input fields and you're able to do createRef() for each field.

But how do you do it if your input fields are dynamically rendered and there could possibly X number of them (depending on the object being mapped)?

Upvotes: 2

Views: 2649

Answers (3)

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15698

You can create an empty array to store your refs. Then as you map over your data to create your inputs, create and push that ref via the ref attribute and call-back.

See working sandbox: https://codesandbox.io/s/interesting-roentgen-q64jl

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const list = [{ name: "apple", label: "seed" }, { name: "you", label: "cat" }];

class App extends React.Component {
  constructor(props) {
    super(props);
    this.refsArr = [];
  }

  focusIntoInput = index => {
    this.refsArr[index].focus();
  };

  render() {
    return (
      <table>
        <tr>
          {list.map(({ name, label }, index) => {
            return (
              <td>
                <input
                  ref={ref => this.refsArr.push(ref)}
                  key={name}
                  type="text"
                  value={label}
                  name={name}
                />
                <span onClick={() => this.focusIntoInput(index)}>Edit</span>
              </td>
            );
          })}
        </tr>
      </table>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

In the focusIntoInput click-handler, the index passed will always correspond to that ref in the refs array.

Upvotes: 1

Ahmad
Ahmad

Reputation: 72673

Why not dynamically create the ref while you map over the object?

{elements.map(() => {
  const ref = React.createRef();
  return <td>
    <input
      key={name}
      type="text"
      value={label}
      name={name}
      ref={ref}
    />
    <span onClick={event => {
      ref.current.focus()
    }}>Edit</span>
  </td>
})}

Upvotes: 1

Jasper Bernales
Jasper Bernales

Reputation: 1681

You can create an multiple refs that maps to each item in your table like the example below.

  const rows = [
    { id: "a", name: "Panel 1" },
    { id: "b", name: "Panel 2" },
    { id: "c", name: "Panel 3" }
  ];

  const refs = useRef(new Map()).current;
  return (
    <div>
      <table>
        {rows.map(row => (
          <tr>
            <td>
              <input
                label={row.name}
                ref={inst =>
                  inst === null ? refs.delete(row.id) : refs.set(row.id, inst)
                }
                type="text"
              />
              <span onClick={() => refs.get(row.id).focus()}>Edit</span>
            </td>
          </tr>
        ))}
      </table>
    </div>

Here is a link to a working sandbox.

Upvotes: 3

Related Questions