Scorri in vista in React

Nov 23 2020

Sto creando una semplice app React dove ce ne sono due differenti div's..

Uno con l'ingresso di selezione e l'elenco selezionato,

  <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 altro elencherà l'opzione selezionata come fieldset,

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

Sulla base di questa soluzione , ho aggiunto createRefall'elemento selezionato come,

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

Quindi ho utilizzato metodi di query Javascript per ottenere elementi DOM come,

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

Aggiunto listener di eventi clic a tutti gli elementi come,

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

Allora scrollSmoothHandlercome,

const scrollDiv = createRef();

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

Ma questo non funziona come previsto.

Requisiti:

Quando si fa clic su qualsiasi elemento in first div, il relativo fieldset deve essere inserito smooth scrolledin un altro div.

Ad esempio: se l'utente fa clic sull'elemento Item Foursotto<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

quindi è necessario scorrere il fieldset correlato. Qui il fieldset con la legenda come Item Four..

Penso che anche rendere i metodi di query js dom su React e non sembra un modo di implementazione React. Qualcuno può aiutarmi gentilmente a ottenere il risultato di scorrere fino a un set di campi correlato facendo clic sull'elemento selezionato ..

Risposte

1 DrewReese Nov 24 2020 at 13:22

Problema

  1. React.createRefè veramente valido solo nei componenti basati su classi. Se utilizzato in un corpo di componente funzionale, il riferimento verrebbe ricreato a ogni ciclo di rendering.
  2. Non utilizzare un selettore di query DOM per collegare onClicklistener agli elementi DOM. Questi vivono fuori reagiscono e dovresti ricordarti di pulirli (cioè rimuoverli) in modo da non avere una perdita di memoria. Usa l' onClickelica di React .
  3. Quando selectedElementsvengono mappati, si collega lo stesso riferimento a ciascun elemento, quindi l'ultimo set è quello che ottiene la tua interfaccia utente.

Soluzione

  1. Utilizzare React.useRefnel corpo del componente funzionale per memorizzare una matrice di riferimenti di reazione da allegare a ciascun elemento che si desidera scorrere nella visualizzazione.
  2. Fissare il scrollSmoothHandlerdirettamente ad ogni span's onClickprop.
  3. Associare ogni riferimento dall'array ref creato in 1. a ciascun set di campi mappato a cui si desidera scorrere.

Codice

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

Soluzione n. 2

Poiché non è possibile aggiornare alcun elemento nel divwith id="container"e tutti i onClickgestori devono essere collegati tramite query al DOM, è comunque possibile utilizzare una scrollSmoothHandlerrichiamata curry e racchiudere un indice nell'ambito. Avrai bisogno di un useEffecthook per interrogare il DOM dopo il rendering iniziale in modo che gli span siano stati montati e un useStatehook per memorizzare uno stato "caricato". Lo stato è necessario per attivare un rerender e racchiudere di nuovo scrollRefsnel 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>
  );
};