useRef for element in loop in react
useRef for element in loop in react
使用 React,我有一个以这种方式静态声明的 ref 列表:
let line1 = useRef(null);
let line2 = useRef(null);
let line3 = useRef(null);
...
//IN MY RENDER PART
<li ref={(el) => (line1 = el)}>line1</li>
<li ref={(el) => (line2 = el)}>line1</li>
<li ref={(el) => (line3 = el)}>line1</li>
然后将引用传递给动画函数,一切正常;
现在情况发生了一些变化,我使用 map 创建了列表项,但我不再能够正确引用该元素;
我试过类似的东西:
{menu.menu.map((D) => {
let newRef = createRef();
LiRefs.push(newRef);
return (
<li
key={D.id}
ref={(el) => (newRef = el)} >
{D.label}
</li>
);
})}
但是我传递给动画函数的数组是空的(我猜是因为该函数是在 useEffect 钩子内部调用的,而 LiRefs 还不是 useRef)
我也知道我将创建的
的数量,所以我可以在开头声明它们,并使用类似 的引用
ref={(el) => (`line${i}` = el)}
这是行不通的
我可以尝试其他解决方案吗?
问题
这不会起作用,因为当 menu
被映射时,每个渲染都会创建新的 React 引用。
解决方案
使用 ref 保存生成的 ref 数组,并在映射时分配它们。
const lineRefs = React.useRef([]);
lineRefs.current = menu.menu.map((_, i) => lineRefs.current[i] ?? createRef());
稍后映射 UI 时,将存储在 lineRefs
中的 react ref 附加到索引 i
{menu.menu.map((D, i) => {
return (
<li
key={D.id}
ref={lineRefs.current[i]} // <-- attach stored ref
{D.label}
</li>
);
})}
我的是 React Hooks 版本。
useMemo
为了性能创建一个引用数组。
const vars = ['a', 'b'];
const childRefs = React.useMemo(
() => vars.map(()=> React.createRef()),
[vars.join(',')]
);
React 会将每个引用挂载到 childRefs
{vars.map((v, i) => {
return (
<div>
<Child v={v} ref={childRefs[i]} />
<button onClick={() => showAlert(i)}> click {i}</button>
</div>
)
})
}
这是一个可行的演示,希望对您有所帮助。 ^_^
<div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="true">
<div class="snippet-code">
<pre><code>const Child = React.forwardRef((props, ref) => {
React.useImperativeHandle(ref, () => ({
showAlert() {
window.alert("Alert from Child: " + props.v);
}
}));
return <h1>Hi, {props.v}</h1>;
});
const App = () => {
const vars = ['a', 'b'];
const childRefs = React.useMemo(
() => vars.map(()=> React.createRef()),
// maybe vars.length
[vars.join(',')]
);
function showAlert(index) {
childRefs[index].current.showAlert();
}
return (
<div>
{
vars.map((v, i) => {
return (
<div>
<Child v={v} ref={childRefs[i]} />
<button onClick={() => showAlert(i)}> click {i}</button>
</div>
)
})
}
</div>
)
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
使用 React,我有一个以这种方式静态声明的 ref 列表:
let line1 = useRef(null);
let line2 = useRef(null);
let line3 = useRef(null);
...
//IN MY RENDER PART
<li ref={(el) => (line1 = el)}>line1</li>
<li ref={(el) => (line2 = el)}>line1</li>
<li ref={(el) => (line3 = el)}>line1</li>
然后将引用传递给动画函数,一切正常; 现在情况发生了一些变化,我使用 map 创建了列表项,但我不再能够正确引用该元素; 我试过类似的东西:
{menu.menu.map((D) => {
let newRef = createRef();
LiRefs.push(newRef);
return (
<li
key={D.id}
ref={(el) => (newRef = el)} >
{D.label}
</li>
);
})}
但是我传递给动画函数的数组是空的(我猜是因为该函数是在 useEffect 钩子内部调用的,而 LiRefs 还不是 useRef) 我也知道我将创建的
ref={(el) => (`line${i}` = el)}
这是行不通的 我可以尝试其他解决方案吗?
问题
这不会起作用,因为当 menu
被映射时,每个渲染都会创建新的 React 引用。
解决方案
使用 ref 保存生成的 ref 数组,并在映射时分配它们。
const lineRefs = React.useRef([]);
lineRefs.current = menu.menu.map((_, i) => lineRefs.current[i] ?? createRef());
稍后映射 UI 时,将存储在 lineRefs
中的 react ref 附加到索引 i
{menu.menu.map((D, i) => {
return (
<li
key={D.id}
ref={lineRefs.current[i]} // <-- attach stored ref
{D.label}
</li>
);
})}
我的是 React Hooks 版本。
useMemo
为了性能创建一个引用数组。
const vars = ['a', 'b'];
const childRefs = React.useMemo(
() => vars.map(()=> React.createRef()),
[vars.join(',')]
);
React 会将每个引用挂载到 childRefs
{vars.map((v, i) => {
return (
<div>
<Child v={v} ref={childRefs[i]} />
<button onClick={() => showAlert(i)}> click {i}</button>
</div>
)
})
}
这是一个可行的演示,希望对您有所帮助。 ^_^
<div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="true">
<div class="snippet-code">
<pre><code>const Child = React.forwardRef((props, ref) => {
React.useImperativeHandle(ref, () => ({
showAlert() {
window.alert("Alert from Child: " + props.v);
}
}));
return <h1>Hi, {props.v}</h1>;
});
const App = () => {
const vars = ['a', 'b'];
const childRefs = React.useMemo(
() => vars.map(()=> React.createRef()),
// maybe vars.length
[vars.join(',')]
);
function showAlert(index) {
childRefs[index].current.showAlert();
}
return (
<div>
{
vars.map((v, i) => {
return (
<div>
<Child v={v} ref={childRefs[i]} />
<button onClick={() => showAlert(i)}> click {i}</button>
</div>
)
})
}
</div>
)
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>