Хорошо, я пытаюсь создать раскрывающийся список, и при выборе элемента страница должна прокручиваться до определенного раздела (ссылка).
У меня есть несколько элементов div с одной и той же ссылкой (поскольку у меня более 30 элементов div, для которых требуется ссылка).
const SettingView = () => {
const selectedItem = "el2"; // This is temporarily as it will be the item from a dropdown
const ref = useRef<HTMLDivElement[]>([]);
const filterRef = (el: HTMLDivElement) => ref.current.push(el);
return (
<>
for (let item of sortedRows) {
<div ref = {filterRef} id = {item.some.name}>{item.text}</div>
}
</>
)
}
export default SettingView;
Таким образом, при нажатии кнопки он должен найти ссылку div, имеющую идентификатор из selectedItem, и прокрутить до него.
Как бы я это сделал?
Этого можно добиться, используя метод ScrollIntoView() для ссылки выбранного элемента. Вот как вы можете изменить свой компонент SettingView для достижения этой функциональности:
import React, { useRef } from 'react';
const SettingView = () => {
const selectedItem = "el2";
const ref = useRef<HTMLDivElement[]>([]);
const filterRef = (el: HTMLDivElement) => ref.current.push(el);
const scrollToRef = (ref: React.RefObject<HTMLDivElement>, id: string) => {
const selectedRef = ref.current.find(item => item.id === id);
if (selectedRef) {
selectedRef.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
};
React.useEffect(() => {
scrollToRef(ref, selectedItem);
}, [selectedItem]);
return (
<>
<div ref = {filterRef} id = "el1">Some Text</div>
<div ref = {filterRef} id = "el2">Some Text2</div>
<div ref = {filterRef} id = "el3">Some Text3</div>
<div ref = {filterRef} id = "el4">Some Text4</div>
</>
);
}
export default SettingView;
В этом коде: Функция ScrollToRef принимает массив ссылок и идентификатор выбранного элемента. Он находит ссылку с соответствующим идентификатором и прокручивает ее в поле зрения с помощью функции ScrollIntoView() с опциями плавного поведения и начиная с начала блока. Хук useEffect используется для запуска эффекта прокрутки при каждом изменении selectedItem.
Если вы визуализируете свои кнопки с помощью map()
, что должно быть нормально, поскольку отличается только идентификатор, тогда вы можете использовать итератор map
, чтобы использовать его в качестве идентификатора.
Затем, нажав кнопку, получите этот индекс в массиве ref.current
и вызовите (например) scrollIntoView()
по нему.
Пример:
scrollIntoView
по этому поводуscrollIntoView
по индексу 50
const { useState, useRef } = React;
const SettingView = () => {
const ref = useRef([]);
let sortedRows = [
{ name: 'Foo', text: 'Bar' }
];
// Add 99 other rows to sortedRow as debug values
sortedRows = sortedRows.flatMap(i => Array(99).fill(i));
const onButtonClick = (i) => {
console.info('Clicked on ', i);
if (ref.current[i]) {
ref.current[i].scrollIntoView();
}
}
return (
<React.Fragment>
<button onClick = {() => onButtonClick(50)}>Scroll to Button #50</button>
{sortedRows.map((row, i) => {
const { name, text } = row;
return (
<div onClick = {() => onButtonClick(i)} ref = {el => ref.current[i] = el} id = {"el" + i}>
Button {i} - {text}
</div>
);
})}
</React.Fragment>
);
}
ReactDOM.render(<SettingView />, document.getElementById("react"));
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id = "react"></div>
Спасибо за ваш ответ. Я не использую индексы, так как раскрывающееся значение представляет собой строку. Он должен совпадать с ссылкой на любой из элементов div.
НЕ уверен, что вы пытаетесь сказать, не имеет значения, что ваши значения являются строками. Все равно лучше визуализировать эти параметры через цикл, что касается DRY.
Элементы div находятся внутри цикла (сгенерированного из массива), так как внутри этих элементов отображается больше данных, но это не имеет значения.
Ну, видимо, это актуально, ха-ха. Я также рендерю из массива, поэтому не знаю, что у вас не работает.
Я обновил свой ответ, чтобы также использовать массив объектов для отображения кнопок. Надеюсь, это прояснит ситуацию.
Еще раз спасибо, я попробовал этот пример, он показывает ошибку в этой части: «ref.current[i]» Введите «HTMLdivElement | null» не может быть присвоен типу «никогда». Тип «ноль» не может быть присвоен типу «никогда».
Я не уверен, что может быть причиной этого, я не эксперт по машинописному тексту. Я удалил синтаксис машинописного текста из своего ответа, чтобы его можно было запустить в фрагменте stackoverflow. Проверьте еще раз, если вы обнаружите какую-либо разницу между ними. ИЛИ, возможно, обновите свой вопрос с помощью текущей попытки.
https://stackblitz.com/edit/stackblitz-starters-tuw9cm?file=src%2FApp.tsx
import { FC, useEffect, useRef, useState } from 'react';
const Div = ({ text, isActive }) => {
const ref = useRef<any>();
useEffect(() => {
if (isActive) {
ref.current.scrollIntoView({ behavior: 'smooth' });
}
}, [isActive]);
return (
<div
ref = {ref}
style = {{
background: isActive ? '#006699' : '#dddddd',
height: '200px',
margin: '10px',
}}
>
{text}
</div>
);
};
export const App: FC<{}> = () => {
const data = [1, 2, 3, 4, 5, 6, 7];
const [activei, setActivei] = useState(5);
return (
<div>
<div>
{data.map((item) => (
<div
onClick = {() => {
setActivei(item);
}}
key = {item}
>
{' '}
{item}{' '}
</div>
))}
</div>
{data.map((item, index) => (
<Div isActive = {item === activei} text = {item} key = {item} />
))}
</div>
);
};
Это было решение, которое сработало для меня лучше всего
Вы сгенерировали этот ответ с помощью такого инструмента, как ChatGPT?