Прокрутите до просмотра в React

Nov 23 2020

Я делаю простое приложение для реагирования, где есть два разных div's..

Один с выбранным входом и выбранным списком,

  <div id="container">
    <div className="_2iA8p44d0WZ">
      <span className="chip _7ahQImy">Item One</span>
      <span className="chip _7ahQImy">Item Two</span>
      <span className="chip _7ahQImy">Item Three</span>
      <span className="chip _7ahQImy">Item Four</span>
      <span className="chip _7ahQImy">Item Five</span>
      <input
        type="text"
        className="searchBox"
        id="search_input"
        placeholder="Select"
        autoComplete="off"
        value=""
      />
    </div>
  </div>

Другой будет перечислять выбранную опцию как fieldset,

  <div>
    {selectedElements.map((item, i) => (
      <div key={i} className="selected-element" ref={scrollDiv}>
        <fieldset>
          <legend>{item}</legend>
        </fieldset>
      </div>
    ))}
  </div>

На основе этого решения я добавил createRefк выбранному элементу, например,

<div key={i} className="selected-element" ref={scrollDiv}>
</div>

Затем я использовал методы запроса Javascript, чтобы получить такие элементы DOM, как,

  const chipsArray = document.querySelectorAll("#container > div > .chip");

Добавлен прослушиватель событий щелчка ко всем элементам, таким как,

  chipsArray.forEach((elem, index) => {
    elem.addEventListener("click", scrollSmoothHandler);
  });

Тогда scrollSmoothHandlerвроде,

const scrollDiv = createRef();

  const scrollSmoothHandler = () => {
    console.log(scrollDiv.current);
    if (scrollDiv.current) {
      scrollDiv.current.scrollIntoView({ behavior: "smooth" });
    }
  };

Но это не так, как ожидалось.

Требование:

При щелчке по любому элементу first div, связанный с ним набор полей должен попасть smooth scrolledв другой div.

Например: если пользователь щелкнет элемент Item Fourпод<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

затем необходимо прокрутить соответствующий набор полей. Здесь набор полей с легендой как Item Four..

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

Ответы

1 DrewReese Nov 24 2020 at 13:22

Проблема

  1. React.createRefдействительно только в компонентах на основе классов. При использовании в теле функционального компонента ссылка будет воссоздаваться в каждом цикле рендеринга.
  2. Не используйте селектор запросов DOM для присоединения onClickслушателей к элементам DOM. Они живут снаружи и реагируют, и вам нужно не забыть очистить их (то есть удалить), чтобы не было утечки памяти. Используйте onClickопору React .
  3. Когда selectedElementsони отображаются, вы прикрепляете один и тот же ref к каждому элементу, поэтому последний набор - это тот, который получает ваш пользовательский интерфейс.

Решение

  1. Используйте React.useRefв теле функционального компонента для хранения массива ссылок на реакцию для присоединения к каждому элементу, который вы хотите просмотреть.
  2. Прикрепление scrollSmoothHandlerнепосредственно к каждому span«S onClickопоре.
  3. Прикрепите каждую ссылку из массива ссылок, созданного в 1. к каждому набору сопоставленных полей, к которому вы хотите прокрутить.

Код

import React, { createRef, useRef } from "react";
import { render } from "react-dom";

const App = () => {
  const selectedElements = [
    "Item One",
    "Item Two",
    "Item Three",
    "Item Four",
    "Item Five"
  ];

  // React ref to store array of refs
  const scrollRefs = useRef([]);

  // Populate scrollable refs, only create them once
  // if the selectedElements array length is expected to change there is a workaround
  scrollRefs.current = [...Array(selectedElements.length).keys()].map(
    (_, i) => scrollRefs.current[i] ?? createRef()
  );

  // Curried handler to take index and return click handler
  const scrollSmoothHandler = (index) => () => {
    scrollRefs.current[index].current.scrollIntoView({ behavior: "smooth" });
  };

  return (
    <div>
      <div id="container">
        <div className="_2iA8p44d0WZ">
          {selectedElements.map((el, i) => (
            <span
              className="chip _7ahQImy"
              onClick={scrollSmoothHandler(i)} // <-- pass index to curried handler
            >
              {el}
            </span>
          ))}
          <input
            type="text"
            className="searchBox"
            id="search_input"
            placeholder="Select"
            autoComplete="off"
            value=""
          />
        </div>
      </div>
      <div>
        {selectedElements.map((item, i) => (
          <div
            key={i}
            className="selected-element"
            ref={scrollRefs.current[i]} // <-- pass scroll ref @ index i
          >
            <fieldset>
              <legend>{item}</legend>
            </fieldset>
          </div>
        ))}
      </div>
    </div>
  );
};

Решение # 2

Поскольку вы не можете обновлять какие-либо элементы в divwith, id="container"а все onClickобработчики должны быть присоединены с помощью запроса к DOM, вы все равно можете использовать каррированный scrollSmoothHandlerобратный вызов и заключить индекс в область видимости. Вам понадобится useEffectловушка для запроса DOM после первоначального рендеринга, чтобы промежутки были смонтированы, и useStateловушка для сохранения «загруженного» состояния. Состояние необходимо для запуска повторной визуализации и повторного включения scrollRefsв scrollSmoothHandlerобратный вызов.

const App = () => {
  const selectedElements = [
    "Item One",
    "Item Two",
    "Item Three",
    "Item Four",
    "Item Five"
  ];

  const [loaded, setLoaded] = useState(false);
  const scrollRefs = useRef([]);

  const scrollSmoothHandler = (index) => () => {
    scrollRefs.current[index].current.scrollIntoView({ behavior: "smooth" });
  };

  useEffect(() => {
    const chipsArray = document.querySelectorAll("#container > div > .chip");

    if (!loaded) {
      scrollRefs.current = [...Array(chipsArray.length).keys()].map(
        (_, i) => scrollRefs.current[i] ?? createRef()
      );

      chipsArray.forEach((elem, index) => {
        elem.addEventListener("click", scrollSmoothHandler(index));
      });
      setLoaded(true);
    }
  }, [loaded]);

  return (
    <div>
      <div id="container">
        <div className="_2iA8p44d0WZ">
          <span className="chip _7ahQImy">Item One</span>
          <span className="chip _7ahQImy">Item Two</span>
          <span className="chip _7ahQImy">Item Three</span>
          <span className="chip _7ahQImy">Item Four</span>
          <span className="chip _7ahQImy">Item Five</span>
          <input
            type="text"
            className="searchBox"
            id="search_input"
            placeholder="Select"
            autoComplete="off"
            value=""
          />
        </div>
      </div>
      <div>
        {selectedElements.map((item, i) => (
          <div key={i} className="selected-element" ref={scrollRefs.current[i]}>
            <fieldset>
              <legend>{item}</legend>
            </fieldset>
          </div>
        ))}
      </div>
    </div>
  );
};