据我所知,我可以像这样为单个元素使用refs:
const { useRef, useState, useEffect } = React;
const App = () => {
const elRef = useRef();
const [elWidth, setElWidth] = useState();
useEffect(() => {
setElWidth(elRef.current.offsetWidth);
}, []);
return (
<div>
<div ref={elRef} style={{ width: "100px" }}>
Width is: {elWidth}
</div>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我如何实现这个元素数组?显然不是这样的:(我知道,即使我没有尝试:)
const { useRef, useState, useEffect } = React;
const App = () => {
const elRef = useRef();
const [elWidth, setElWidth] = useState();
useEffect(() => {
setElWidth(elRef.current.offsetWidth);
}, []);
return (
<div>
{[1, 2, 3].map(el => (
<div ref={elRef} style={{ width: `${el * 100}px` }}>
Width is: {elWidth}
</div>
))}
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我见过这个,所以才会这样。但是,我仍然不知道如何在这个简单的案例中实现这个建议。
如果我理解正确的话,useEffect应该只用于副作用,因此我选择使用useMemo。
const App = props => {
const itemsRef = useMemo(() => Array(props.items.length).fill().map(() => createRef()), [props.items]);
return props.items.map((item, i) => (
<div
key={i}
ref={itemsRef[i]}
style={{ width: `${(i + 1) * 100}px` }}>
...
</div>
));
};
然后如果你想操纵物品/使用副作用,你可以这样做:
useEffect(() => {
itemsRef.map(e => e.current).forEach((e, i) => { ... });
}, [itemsRef.length])
通过将子元素移动到单独的组件中,可以避免数组引用与useEffect结合带来的复杂性。这还有其他优点,主要是可读性强,易于维护。
const { useRef, useState, useEffect } = React;
const ListComponent = ({ el }) => {
const elRef = useRef();
const [elWidth, setElWidth] = useState();
useEffect(() => {
setElWidth(elRef.current.offsetWidth);
}, []);
return (
<div ref={elRef} style={{ width: `${el * 100}px` }}>
Width is: {elWidth}
</div>
);
};
const App = () => {
return (
<div>
{[1, 2, 3].map((el, i) => (
<ListComponent key={i} el={el} />
))}
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
您可以使用数组(或对象)来跟踪所有的引用,并使用方法向数组添加ref。
注意:如果你添加和删除引用,你必须在每个渲染周期清空数组。
import React, { useRef } from "react";
const MyComponent = () => {
// intialize as en empty array
const refs = useRefs([]); // or an {}
// Make it empty at every render cycle as we will get the full list of it at the end of the render cycle
refs.current = []; // or an {}
// since it is an array we need to method to add the refs
const addToRefs = el => {
if (el && !refs.current.includes(el)) {
refs.current.push(el);
}
};
return (
<div className="App">
{[1,2,3,4].map(val => (
<div key={val} ref={addToRefs}>
{val}
</div>
))}
</div>
);
}
工作示例
https://codesandbox.io/s/serene-hermann-kqpsu
我使用useRef钩子创建我想独立控制的数据面板。首先我初始化useRef来存储一个数组:
import React, { useRef } from "react";
const arr = [1, 2, 3];
const refs = useRef([])
在初始化数组时,我们观察到它实际上是这样的:
//refs = {current: []}
然后我们应用map函数来创建面板,使用我们将引用的div标签,将当前元素添加到我们的引用中。带有一个按钮的当前数组:
arr.map((item, index) => {
<div key={index} ref={(element) => {refs.current[index] = element}}>
{item}
<a
href="#"
onClick={(e) => {
e.preventDefault();
onClick(index)
}}
>
Review
</a>
})
最后一个函数接收按下按钮的索引,我们可以控制我们想要显示的面板
const onClick = (index) => {
console.log(index)
console.log(refs.current[index])
}
最后,完整的代码是这样的
import React, { useRef } from "react";
const arr = [1, 2, 3];
const refs = useRef([])
//refs = {current: []}
const onClick = (index) => {
console.log(index)
console.log(refs.current[index])
}
const MyPage = () => {
const content = arr.map((item, index) => {
<div key={index} ref={(element) => {refs.current[index] = element}}>
{item}
<a
href="#"
onClick={(e) => {
e.preventDefault();
onClick(index)
}}
>
Review
</a>
})
return content
}
export default MyPage
这对我很管用!希望这些知识对你有用。