Faites défiler la vue en réagissant

Nov 23 2020

Je crée une application de réaction simple où il y en a deux div's.

Un avec sélection d'entrée et liste sélectionnée,

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

Un autre listera l'option sélectionnée comme suit fieldset:

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

Sur la base de cette solution , j'ai ajouté createRefà l'élément sélectionné comme,

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

Ensuite, j'ai utilisé des méthodes de requête Javascript pour obtenir des éléments DOM comme,

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

Ajout d'un écouteur d'événement de clic à tous les éléments comme,

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

Alors scrollSmoothHandlercomme,

const scrollDiv = createRef();

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

Mais cela ne fonctionne pas comme prévu.

Exigence:

first divLorsque vous cliquez sur un élément dans , le jeu de champs associé doit entrer smooth scrolleddans un autre div.

Par exemple: si l'utilisateur clique sur l'élément Item Foursous<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

alors le jeu de champs associé doit être défilé. Ici le fieldset avec la légende comme Item Four..

Je pense aussi faire réagir les méthodes de requête js dom et cela ne semble pas être une manière de réagir. Quelqu'un peut-il s'il vous plaît m'aider à obtenir le résultat du défilement vers un jeu de champs associé en cliquant sur l'élément sélectionné.

Réponses

1 DrewReese Nov 24 2020 at 13:22

Problème

  1. React.createRefn'est vraiment valable que dans les composants basés sur les classes. S'il était utilisé dans un corps de composant fonctionnel, la référence serait recréée à chaque cycle de rendu.
  2. N'utilisez pas de sélecteur de requête DOM pour attacher des onClickécouteurs aux éléments DOM. Ceux-ci vivent à l'extérieur et vous devez vous rappeler de les nettoyer (c'est-à-dire de les supprimer) pour ne pas avoir de fuite de mémoire. Utilisez l' onClickaccessoire de React .
  3. Lorsque les selectedElementssont mappés, vous attachez la même référence à chaque élément, donc le dernier ensemble est celui que votre interface utilisateur obtient.

Solution

  1. Utilisez-le React.useRefdans le corps du composant fonctionnel pour stocker un tableau de références de réaction à attacher à chaque élément que vous souhaitez afficher.
  2. Fixez le scrollSmoothHandlerdirectement à chaque spande » onClickl'hélice.
  3. Attachez chaque référence du tableau de références créé en 1. à chaque jeu de champs mappé vers lequel vous voulez faire défiler.

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

Solution n ° 2

Étant donné que vous ne pouvez mettre à jour aucun élément dans le divavec id="container"et que tous les onClickgestionnaires doivent être attachés via l'interrogation du DOM, vous pouvez toujours utiliser un scrollSmoothHandlerrappel curry et inclure un index dans la portée. Vous aurez besoin d'un useEffecthook pour interroger le DOM après le rendu initial afin que les travées aient été montées, et d'un useStatehook pour stocker un état "chargé". L'état est nécessaire pour déclencher un rerender et recouvrir le scrollRefsdans le scrollSmoothHandlercallback.

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