Reactjs: программно фокусируется на программно сгенерированном вводе - PullRequest
0 голосов
/ 10 октября 2019

У меня есть приложение, которое отображает редактируемую таблицу с несколькими входными элементами на строку. Поскольку таблица заполнена пользовательскими записями, она может иметь переменную длину. Некоторые записи пользователя в таблице будут недействительными. Когда запись недействительна, я хочу отобразить предупреждение «Вы ввели недопустимые данные», а затем сфокусировать пользователя на вводе с недопустимой записью.

При чтении документации будет отображаться ссылкабыть лучшим способом сделать вещь. Тем не менее, во всех примерах, которые я нашел, будь то из команды React или из Stack Overflow, показано, что один вход привязан к одной ссылке. Некоторое действие (обычно нажатие кнопки) жестко запрограммировано для ссылки на конкретную ссылку, и все работает отлично.

Я бы хотел определить наилучший способ доступа к соответствующему входу при ошибке. «Лучший» способ сделать это, по-видимому, создать массив ссылок, затем назначить ссылку для каждого входа - но в документах также есть смысл говорить об использовании ссылок редко, и это похоже на грубое неправильное использование ссылок.

Я бы предпочел сделать что-то вроде создания единой ссылки, а затем переназначить ее на какой-либо последний обновленный ввод. Каждый раз, когда пользователь теряет фокус, он проверяет, принудительно ли фокусироваться на вводе с неверными данными. Такое ощущение, что у него есть потенциал для противодействия паттерну, тем более что я не могу найти никаких доказательств того, что кто-то еще его реализует.

Мне бы хотелось узнать лучший способ решения этой проблемы, так как я боюсьчто ни одна из моих идей не является хорошим путем вперед.

1 Ответ

0 голосов
/ 11 октября 2019

Вы можете положиться не на одну единственную ссылку, а на ссылку контейнера. У вас всегда есть ссылка на один контейнер, и всякий раз, когда пользователь стирает данные на входе, вы должны получить все входные данные из контейнера, используя .querySelectorAll('input'), найти неверный и сфокусировать его.

const {
  useState,
  useCallback,
  useMemo,
  useRef,
  useEffect
} = React;

const defaultUsers = [
  { id: 1, name: "User #1", email: "user1@gmail.com" },
  { id: 2, name: "User #2", email: "user2@gmail.com" },
  { id: 3, name: "User #3", email: "user3@gmail.com" },
  { id: 4, name: "User #4", email: "user4@gmail.com" },
  { id: 5, name: "", email: "user5@gmail.com" },
  { id: 6, name: "User #6", email: "user6@gmail.com" },
  { id: 7, name: "User #7", email: "user7@gmail.com" },
  { id: 8, name: "User #8", email: "" },
  { id: 9, name: "User #9", email: "user9@gmail.com" },
  { id: 10, name: "User #10", email: "user10@gmail.com" }
];

const App = () => {
  const tbodyRef = useRef();

  const focusNextInvalidInput = useCallback(() => {
    if (tbodyRef.current) {
      const inputs = tbodyRef.current.querySelectorAll("td > input");
      for (const input of inputs) {
        if (input.value.length === 0) {
          input.focus();
          return;
        }
      }
    }
  }, [tbodyRef]);

  useEffect(() => {
    focusNextInvalidInput();
  }, [focusNextInvalidInput]);

  const [users, setUsers] = useState(defaultUsers);

  const updateUserField = useCallback(
    (id, field, value) =>
      setUsers(
        users.map(user => (user.id === id ? { ...user, [field]: value } : user))
      ),
    [users]
  );

  const changeUserName = useCallback(
    (id, name) => updateUserField(id, "name", name),
    [updateUserField]
  );
  const changeUserEmail = useCallback(
    (id, email) => updateUserField(id, "email", email),
    [updateUserField]
  );

  const usersRows = useMemo(() => {
    return users.map(({ id, name, email }) => (
      <tr key={id}>
        <td>{id}</td>
        <td>
          <input
            type="text"
            onChange={e => changeUserName(id, e.target.value)}
            value={name}
            onBlur={focusNextInvalidInput}
          />
        </td>
        <td>
          <input
            type="email"
            onChange={e => changeUserEmail(id, e.target.value)}
            value={email}
            onBlur={focusNextInvalidInput}
          />
        </td>
      </tr>
    ));
  }, [users, changeUserEmail, changeUserName, focusNextInvalidInput]);

  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Email</th>
        </tr>
      </thead>
      <tbody ref={tbodyRef}>{usersRows}</tbody>
    </table>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Пример CodeSandbox .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...