Scrollen Sie in die Ansicht, um zu reagieren

Nov 23 2020

Ich mache eine einfache Reaktions-App, in der es zwei verschiedene gibt div's.

Eine mit Auswahleingabe und ausgewählter Liste,

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

Ein anderer wird die gewählte Option Liste unten als fieldset,

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

Basierend auf dieser Lösung habe ich createRefdem ausgewählten Element Folgendes hinzugefügt :

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

Dann habe ich Javascript-Abfragemethoden verwendet, um DOM-Elemente wie:

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

Klickereignis-Listener zu allen Elementen wie hinzugefügt,

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

Dann scrollSmoothHandlerwie,

const scrollDiv = createRef();

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

Dies funktioniert jedoch nicht wie erwartet.

Anforderung:

first divWenn Sie auf ein Element in klicken , muss das zugehörige Feldset smooth scrolledin ein anderes Div gelangen.

Beispiel: Wenn der Benutzer auf das Element Item Fourunter klickt<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

dann muss das zugehörige Feldset gescrollt werden. Hier das Feldset mit Legende als Item Four..

Ich denke auch, dass die js dom-Abfragemethoden reagieren und es scheint keine reaktive Art der Implementierung zu sein. Kann mir bitte jemand helfen, das Ergebnis des Bildlaufs zu einem verwandten Feldsatz zu erzielen, indem Sie auf das ausgewählte Element klicken.

Antworten

1 DrewReese Nov 24 2020 at 13:22

Problem

  1. React.createRefist wirklich nur in klassenbasierten Komponenten gültig. Bei Verwendung in einem Funktionskomponentenkörper wird der Verweis bei jedem Renderzyklus neu erstellt.
  2. Verwenden Sie keinen DOM-Abfrageselektor, um onClickListener an DOM-Elemente anzuhängen . Diese leben draußen und Sie müssen daran denken, sie zu bereinigen (dh zu entfernen), damit Sie keinen Speicherverlust haben. Verwenden Sie die onClickRequisite von React .
  3. Wenn die zugeordnet selectedElementssind, fügen Sie jedem Element den gleichen Verweis hinzu, sodass der letzte Satz derjenige ist, den Ihre Benutzeroberfläche erhält.

Lösung

  1. Verwenden Sie diese React.useRefFunktion im Funktionskomponentenkörper, um ein Array von Reaktionsreferenzen zu speichern, die an jedes Element angehängt werden sollen, in das Sie scrollen möchten.
  2. Bringen Sie die scrollSmoothHandlerdirekt an den jeweiligen spans‘ onClickprop.
  3. Fügen Sie jede Referenz aus dem in 1. erstellten Referenzarray jedem zugeordneten Feldsatz hinzu, zu dem Sie einen Bildlauf durchführen möchten.

Code

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

Lösung Nr. 2

Da Sie keine Elemente im divwith aktualisieren können id="container"und alle onClickHandler über die Abfrage des DOM angehängt werden müssen, können Sie weiterhin einen Curry- scrollSmoothHandlerRückruf verwenden und einen Index in den Bereich einschließen. Sie benötigen einen useEffectHook, um das DOM nach dem ersten Rendern abzufragen , damit die Bereiche bereitgestellt wurden, und einen useStateHook, um einen "geladenen" Status zu speichern. Der Status ist erforderlich, um ein erneutes Rendern auszulösen und das scrollRefsim scrollSmoothHandlerRückruf wieder einzuschließen .

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