滚动到视图以做出反应 [英] Scroll into view in react
问题描述
我正在制作一个简单的react应用,其中有两个不同的 div's
..
具有选择输入和选定列表的一个,
< div id ="container">< div className ="_ 2iA8p44d0WZ">< span className =芯片_7ahQImy">项目One</span>< span className =芯片_7ahQImy">项目2</span>< span className =芯片_7ahQImy">项目3</span>< span className =芯片_7ahQImy">项目四</span>< span className =芯片_7ahQImy">项目5</span><输入type ="text"className =" searchBox"id ="search_input";占位符=选择"autoComplete =关闭".值="/></div></div>
另一个人将选择的选项列为 fieldset
,
< div>{selectedElements.map((item,i)=>(< div键= {i} className =选定元素";ref = {scrollDiv}>< fieldset>< legend> {item}</legend></fieldset></div>))}</div>
基于
问题
-
React.createRef
实际上仅在基于类的组件中有效.如果在功能组件主体中使用,则将在每个渲染周期重新创建引用. - 不要使用DOM查询选择器将
onClick
侦听器附加到DOM元素.这些住在外面的生物会做出反应,您需要记住将它们清理干净(即删除它们),以免发生内存泄漏.使用React的onClick
道具. - 映射
selectedElements
时,您将 same 引用附加到每个元素,因此最后一组是用户界面获得的一组.
解决方案
- 在功能组件主体中使用
React.useRef
来存储一组反应引用,以附加到要滚动到视图中的每个元素. - 将
scrollSmoothHandler
直接附加到每个span
的onClick
道具上. - 将1.中创建的ref数组中的每个ref附加到要滚动到的每个映射字段集.
代码
从"react"导入React,{createRef,useRef};从"react-dom"导入{render};const App =()=>{const selectedElements = [项目一",第二项",第3项",第四项",<项目五>];//反应参考以存储参考数组const scrollRefs = useRef([]);//填充可滚动引用,仅创建一次//如果selectedElements数组的长度预计会发生变化,则有一种解决方法scrollRefs.current = [... Array(selectedElements.length).keys()].map((_,i)=>scrollRefs.current [i]?createRef());//咖喱句柄获取索引并返回click句柄const scrollSmoothHandler =(索引)=>()=>{scrollRefs.current [index] .current.scrollIntoView({behavior:"smooth"});};返回 (< div>< div id =容器">< div className ="_ 2iA8p44d0WZ">{selectedElements.map((el,i)=>(< spanclassName =芯片_7ahQImy";onClick = {scrollSmoothHandler(i)}//<-将索引传递给已处理的处理程序>{el}</span>))}<输入type ="text"className =" searchBox"id ="search_input";占位符=选择"autoComplete =关闭".值="/></div></div>< div>{selectedElements.map((item,i)=>(÷ div键= {i}className =选定元素";ref = {scrollRefs.current [i]}//<-通过滚动ref @ index i>< fieldset>< legend> {item}</legend></fieldset></div>))}</div></div>);};
解决方案2
由于您无法使用 id ="container"
更新 div
中的任何元素,因此所有 onClick
处理程序都需要通过查询DOM附加,您仍然可以使用咖喱的 scrollSmoothHandler
回调并将索引包含在作用域中.您需要一个 useEffect
钩子来查询DOM,然后在初始渲染后查询DOM,以便跨度已被挂载,还需要一个 useState
钩子来存储DOM.已加载"状态.该状态是触发重新渲染并重新封装在 scrollSmoothHandler
回调中的 scrollRefs
上所必需的.
const App =()=>{const selectedElements = [项目一",第二项",第3项",第四项",<项目五>];const [loaded,setLoaded] = useState(false);const scrollRefs = useRef([]);const scrollSmoothHandler =(索引)=>()=>{scrollRefs.current [index] .current.scrollIntoView({behavior:"smooth"});};useEffect(()=> {const chipArray = document.querySelectorAll(#container> div>..chip");如果(!已加载){scrollRefs.current = [... Array(chipsArray.length).keys()].map((_,i)=>scrollRefs.current [i]?createRef());chipArray.forEach((elem,index)=> {elem.addEventListener("click",scrollSmoothHandler(index));});setLoaded(true);}},[已加载]);返回 (< div>< div id =容器">< div className ="_ 2iA8p44d0WZ">< span className =芯片_7ahQImy">项目One</span>< span className =芯片_7ahQImy">项目2</span>< span className =芯片_7ahQImy">项目3</span>< span className =芯片_7ahQImy">项目四</span>< span className =芯片_7ahQImy">项目5</span><输入type ="text"className =" searchBox"id ="search_input";占位符=选择"autoComplete =关闭".值="/></div></div>< div>{selectedElements.map((item,i)=>(< div键= {i} className =选定元素";ref = {scrollRefs.current [i]}>< fieldset>< legend> {item}</legend></fieldset></div>))}</div></div>);};
I am making a simple react app where there are two different div's
..
One with select input and selected list,
<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>
Another will list down the selected option as fieldset
,
<div>
{selectedElements.map((item, i) => (
<div key={i} className="selected-element" ref={scrollDiv}>
<fieldset>
<legend>{item}</legend>
</fieldset>
</div>
))}
</div>
Based on this solution, I have added createRef
to the selected element like,
<div key={i} className="selected-element" ref={scrollDiv}>
</div>
Then I took Javascript query methods to get DOM elements like,
const chipsArray = document.querySelectorAll("#container > div > .chip");
Added click event listener to all the elements like,
chipsArray.forEach((elem, index) => {
elem.addEventListener("click", scrollSmoothHandler);
});
Then scrollSmoothHandler
like,
const scrollDiv = createRef();
const scrollSmoothHandler = () => {
console.log(scrollDiv.current);
if (scrollDiv.current) {
scrollDiv.current.scrollIntoView({ behavior: "smooth" });
}
};
But this doesn't work the way as expected.
Requirement:
On click over any item in first div
, then its related fieldset needs to get smooth scrolled
in another div..
Eg:
If user click on the element Item Four
under
<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>
then the related fieldset needs to get scrolled into. Here the fieldset with legend as Item Four
..
I think also making the js dom query methods on react and it seems not a react way of implementation. Can anyone please kindly help me to achieve the result of scrolling to a related fieldset on click over the selected item..
Issue
React.createRef
is really only valid in class-based components. If used in a functional component body then the ref would be recreated each render cycle.- Don't use a DOM query selector to attach
onClick
listeners to DOM elements. These live outside react and you'd need to remember to clean them up (i.e. remove them) so you don't have a memory leak. Use React'sonClick
prop. - When the
selectedElements
are mapped you attach the same ref to each element, so the last one set is the one your UI gets.
Solution
- Use
React.useRef
in the functional component body to store an array of react refs to attach to each element you want to scroll into view. - Attach the
scrollSmoothHandler
directly to eachspan
'sonClick
prop. - Attach each ref from the ref array created in 1. to each mapped field set you want to scroll to.
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 #2
Since you can't update any elements in the div
with id="container"
and all the onClick
handlers need to be attached via querying the DOM, you can still use a curried scrollSmoothHandler
callback and enclose an index in scope. You'll need an useEffect
hook to query the DOM after the initial render so the spans have been mounted, and an useState
hook to store a "loaded" state. The state is necessary to trigger a rerender and re-enclose over the scrollRefs
in the scrollSmoothHandler
callback.
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>
);
};
这篇关于滚动到视图以做出反应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!