反応でビューにスクロールします
私は2つの異なるものがある簡単なreactアプリを作っていdiv's
ます..
選択された入力と選択されたリストを持つもの、
<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>
別の人は、選択したオプションをfieldset
、としてリストします
<div>
{selectedElements.map((item, i) => (
<div key={i} className="selected-element" ref={scrollDiv}>
<fieldset>
<legend>{item}</legend>
</fieldset>
</div>
))}
</div>
このソリューションに基づいて、次のcreateRef
ような選択した要素に追加しました。
<div key={i} className="selected-element" ref={scrollDiv}>
</div>
次に、Javascriptクエリメソッドを使用して、次のようなDOM要素を取得しました。
const chipsArray = document.querySelectorAll("#container > div > .chip");
次のようなすべての要素にクリックイベントリスナーを追加しました。
chipsArray.forEach((elem, index) => {
elem.addEventListener("click", scrollSmoothHandler);
});
次に、のscrollSmoothHandler
ように、
const scrollDiv = createRef();
const scrollSmoothHandler = () => {
console.log(scrollDiv.current);
if (scrollDiv.current) {
scrollDiv.current.scrollIntoView({ behavior: "smooth" });
}
};
しかし、これは期待どおりに機能しません。
要件:
内の任意のアイテムをクリックするとfirst div
、それに関連するフィールドセットがsmooth scrolled
別のdivに入る必要があります。
例:ユーザーがItem Four
下の要素をクリックした場合<div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>
次に、関連するフィールドセットをスクロールする必要があります。ここでは、凡例がItem Four
..のフィールドセットです。
また、reactでjs domクエリメソッドを作成していると思いますが、これはreactの実装方法ではないようです。選択したアイテムをクリックすると、関連するフィールドセットにスクロールした結果が得られるように、誰か助けてくれませんか。
回答
問題
React.createRef
実際には、クラスベースのコンポーネントでのみ有効です。機能コンポーネント本体で使用される場合、参照はレンダリングサイクルごとに再作成されます。onClick
リスナーをDOM要素にアタッチするためにDOMクエリセレクターを使用しないでください。これらは外部で反応するため、メモリリークが発生しないように、クリーンアップ(つまり削除)することを忘れないでください。ReactのonClick
小道具を使用してください。- ときに
selectedElements
マッピングされていますが添付同じ各要素に参照し、その最後の1セットは、あなたのUIが得るものです。
解決
React.useRef
機能コンポーネント本体で使用して、スクロールして表示する各要素にアタッチする反応参照の配列を格納します。scrollSmoothHandler
それぞれspan
のonClick
支柱に直接取り付けます。- 1.で作成したref配列の各refを、スクロール先のマップされた各フィールドセットに添付します。
コード
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>
);
};
解決策#2
div
withの要素を更新することはできずid="container"
、すべてのonClick
ハンドラーをDOMのクエリを介してアタッチする必要があるため、カレーscrollSmoothHandler
コールバックを使用して、スコープ内のインデックスを囲むことができます。スパンがマウントされるように最初のレンダリング後useEffect
にDOMをクエリするためのフックと、「ロードされた」状態を格納するためのフックが必要になります。状態は、再レンダリングをトリガーし、コールバックでを再囲むために必要です。useState
scrollRefs
scrollSmoothHandler
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>
);
};