Przewiń do widoku w Reaguj

Nov 23 2020

Tworzę prostą aplikację do reagowania, w której są dwa różne div's...

Jeden z wybranym wejściem i wybraną listą,

  <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>

Inny wyświetli wybraną opcję jako fieldset,

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

Bazując na tym rozwiązaniu dodałem createRefdo wybranego elementu np.

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

Następnie skorzystałem z metod zapytań Javascript, aby uzyskać elementy DOM, takie jak,

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

Dodano detektor kliknięć do wszystkich elementów, takich jak,

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

Następnie scrollSmoothHandlerjak

const scrollDiv = createRef();

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

Ale to nie działa zgodnie z oczekiwaniami.

Wymaganie:

Po kliknięciu dowolnego elementu w first div, powiązany zestaw pól musi przejść smooth scrolleddo innego div.

Np .: Jeśli użytkownik kliknie element Item Fourpod<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

następnie powiązany zestaw pól musi zostać przewinięty do. Tutaj zestaw pól z legendą jako Item Four...

Myślę również, że wykonanie zapytań js dom metodami na reaguje i wydaje się, że nie jest to reakcja na sposób implementacji. Czy ktoś może mi uprzejmie pomóc w osiągnięciu wyniku przewijania do odpowiedniego zestawu pól po kliknięciu wybranej pozycji.

Odpowiedzi

1 DrewReese Nov 24 2020 at 13:22

Kwestia

  1. React.createRefjest tak naprawdę ważne tylko w komponentach opartych na klasach. Jeśli zostanie użyty w korpusie komponentu funkcjonalnego, ref będzie odtwarzany w każdym cyklu renderowania.
  2. Nie używaj selektora zapytań DOM do dołączania onClickdetektorów do elementów DOM. Te żyjące na zewnątrz reagują i musisz pamiętać, aby je wyczyścić (tj. Usunąć), aby nie doszło do wycieku pamięci. Użyj onClickrekwizytu Reacta.
  3. Kiedy selectedElementssą mapowane, dołączasz ten sam odnośnik do każdego elementu, więc ostatni zestaw jest tym, który dostaje twój interfejs użytkownika.

Rozwiązanie

  1. Użyj React.useRefw treści komponentu funkcjonalnego, aby przechowywać tablicę odwołań do reakcji, które mają być dołączone do każdego elementu, który chcesz przewinąć do widoku.
  2. Mocowanie scrollSmoothHandlerbezpośrednio do siebie span„s onClickpodpory.
  3. Dołącz każdy ref z tablicy ref utworzonej w 1. do każdego odwzorowanego zestawu pól, do którego chcesz przewinąć.

Kod

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>
  );
};

Rozwiązanie nr 2

Ponieważ nie możesz aktualizować żadnych elementów w divwith, id="container"a wszystkie onClickprogramy obsługujące muszą być dołączone poprzez zapytanie do DOM, nadal możesz użyć scrollSmoothHandlerwywołania zwrotnego curried i zawrzeć indeks w zakresie. Będziesz potrzebował useEffecthaka do wysyłania zapytań do DOM po początkowym renderowaniu, aby rozpiętości zostały zamontowane, oraz useStatehaka do przechowywania stanu „załadowanego”. Stan jest niezbędny do wyzwolenia ponownego wysłania i ponownego zamknięcia scrollRefsw scrollSmoothHandlerwywołaniu zwrotnym.

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>
  );
};