Cuộn vào chế độ xem trong phản ứng

Nov 23 2020

Tôi đang tạo một ứng dụng phản ứng đơn giản có hai ứng dụng khác nhau div's..

Một với đầu vào được chọn và danh sách đã chọn,

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

Một tùy chọn khác sẽ liệt kê tùy chọn đã chọn dưới dạng fieldset,

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

Dựa trên giải pháp này , tôi đã thêm vào createRefphần tử đã chọn như,

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

Sau đó, tôi sử dụng các phương thức truy vấn Javascript để nhận các phần tử DOM như,

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

Đã thêm trình nghe sự kiện nhấp chuột vào tất cả các yếu tố như,

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

Sau đó scrollSmoothHandlernhư,

const scrollDiv = createRef();

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

Nhưng điều này không hoạt động theo cách như mong đợi.

Yêu cầu:

Khi nhấp vào bất kỳ mục nào trong first divđó, thì tập trường liên quan của nó cần phải có smooth scrolledtrong một div khác ..

Vd: Nếu người dùng nhấp vào phần tử Item Fourbên dưới<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

thì tập trường liên quan cần được cuộn vào. Đây là bộ trường với chú giải là Item Four..

Tôi nghĩ rằng cũng thực hiện các phương thức truy vấn js dom trên phản ứng và nó có vẻ không phải là một cách thực hiện phản ứng. Bất cứ ai có thể vui lòng giúp tôi để đạt được kết quả của việc cuộn đến một bộ trường liên quan khi nhấp vào mục đã chọn ..

Trả lời

1 DrewReese Nov 24 2020 at 13:22

Vấn đề

  1. React.createRefthực sự chỉ hợp lệ trong các thành phần dựa trên lớp. Nếu được sử dụng trong thân thành phần chức năng thì tham chiếu sẽ được tạo lại mỗi chu kỳ hiển thị.
  2. Không sử dụng bộ chọn truy vấn DOM để đính kèm trình onClicknghe vào các phần tử DOM. Những thứ này sống bên ngoài sẽ phản ứng và bạn cần phải nhớ dọn dẹp chúng (tức là loại bỏ chúng) để không bị rò rỉ bộ nhớ. Sử dụng phần mềm onClickhỗ trợ React .
  3. Khi selectedElementsánh xạ được ánh xạ, bạn đính kèm cùng một tham chiếu vào từng phần tử, vì vậy tập hợp cuối cùng là tập hợp mà giao diện người dùng của bạn nhận được.

Giải pháp

  1. Sử dụng React.useReftrong phần thân thành phần chức năng để lưu trữ một loạt các phản ứng tham chiếu để đính kèm vào mỗi phần tử bạn muốn cuộn vào chế độ xem.
  2. Gắn scrollSmoothHandlertrực tiếp với nhau span's onClickprop.
  3. Đính kèm từng tham chiếu từ mảng tham chiếu được tạo trong 1. vào mỗi tập trường được ánh xạ mà bạn muốn cuộn đến.

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

Giải pháp # 2

Vì bạn không thể cập nhật bất kỳ phần tử nào trong divwith id="container"và tất cả các onClicktrình xử lý cần được đính kèm thông qua truy vấn DOM, nên bạn vẫn có thể sử dụng một lệnh scrollSmoothHandlergọi lại được sắp xếp và bao gồm một chỉ mục trong phạm vi. Bạn sẽ cần một useEffecthook để truy vấn DOM sau lần hiển thị đầu tiên để các nhịp đã được gắn kết và một useStatehook để lưu trữ trạng thái "đã tải". Trạng thái là cần thiết để kích hoạt một kết xuất và bao bọc lại scrollRefstrong lệnh scrollSmoothHandlergọi lại.

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