据我所知,我可以像这样为单个元素使用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结合带来的复杂性。这还有其他优点,主要是可读性强,易于维护。
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最初只是一个{current: null}对象。useRef在组件渲染之间保持对该对象的引用。当前值主要用于组件引用,但可以保存任何内容。
在某些时候应该有一个refs数组。如果数组的长度可能在不同的渲染之间变化,数组应该相应地缩放:
const arrLength = arr.length;
const [elRefs, setElRefs] = React.useState([]);
React.useEffect(() => {
// add or remove refs
setElRefs((elRefs) =>
Array(arrLength)
.fill()
.map((_, i) => elRefs[i] || createRef()),
);
}, [arrLength]);
return (
<div>
{arr.map((el, i) => (
<div ref={elRefs[i]} style={...}>
...
</div>
))}
</div>
);
这段代码可以通过展开useEffect并将useState替换为useRef来优化,但应该注意的是,在呈现函数中执行副作用通常被认为是一种糟糕的做法:
const arrLength = arr.length;
const elRefs = React.useRef([]);
if (elRefs.current.length !== arrLength) {
// add or remove refs
elRefs.current = Array(arrLength)
.fill()
.map((_, i) => elRefs.current[i] || createRef());
}
return (
<div>
{arr.map((el, i) => (
<div ref={elRefs.current[i]} style={...}>
...
</div>
))}
</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])