据我所知,我可以像这样为单个元素使用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>

我见过这个,所以才会这样。但是,我仍然不知道如何在这个简单的案例中实现这个建议。


当前回答

以上所有其他选项都依赖于数组,但这使得事情非常脆弱,因为元素可能会重新排序,然后我们就不跟踪ref属于哪个元素了。

React使用键道具来跟踪项目。因此,如果你按键存储你的引用,就不会有任何问题:

const useRefs = () => {
  const refs = useRef<Record<string,HTMLElement | null>>({})

  const setRefFromKey = (key: string) => (element: HTMLElement | null) => {
    refs.current[key] = element;
  }

  return {refs: refs.current, setRefFromKey};
}
const Comp = ({ items }) => {
  const {refs, setRefFromKey} = useRefs()

  const refsArr = Object.values(refs) // your array of refs here

  return (
    <div>
      {items.map(item => (
        <div key={item.id} ref={setRefFromKey(item.id)}/>
      )}
    </div>
  )
}

请注意,React在卸载一个项目时,将使用null调用所提供的函数,该函数将在对象中将匹配的键项设置为null,因此所有内容都将是最新的。

其他回答

假设您的数组包含非原语,您可以使用WeakMap作为Ref的值。

function MyComp(props) {
    const itemsRef = React.useRef(new WeakMap())

    // access an item's ref using itemsRef.get(someItem)

    render (
        <ul>
            {props.items.map(item => (
                <li ref={el => itemsRef.current.set(item, el)}>
                    {item.label}
                </li>
            )}
        </ul>
    )
}

如果我理解正确的话,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])

以上所有其他选项都依赖于数组,但这使得事情非常脆弱,因为元素可能会重新排序,然后我们就不跟踪ref属于哪个元素了。

React使用键道具来跟踪项目。因此,如果你按键存储你的引用,就不会有任何问题:

const useRefs = () => {
  const refs = useRef<Record<string,HTMLElement | null>>({})

  const setRefFromKey = (key: string) => (element: HTMLElement | null) => {
    refs.current[key] = element;
  }

  return {refs: refs.current, setRefFromKey};
}
const Comp = ({ items }) => {
  const {refs, setRefFromKey} = useRefs()

  const refsArr = Object.values(refs) // your array of refs here

  return (
    <div>
      {items.map(item => (
        <div key={item.id} ref={setRefFromKey(item.id)}/>
      )}
    </div>
  )
}

请注意,React在卸载一个项目时,将使用null调用所提供的函数,该函数将在对象中将匹配的键项设置为null,因此所有内容都将是最新的。

更新

新React文档显示了一个使用地图的推荐方法。

点击这里查看测试版(2022年12月)


有两种方法

对多个当前元素使用一个引用

const inputRef = useRef([]);

inputRef.current[idx].focus();

<input
  ref={el => inputRef.current[idx] = el}
/>

const {useRef} = React; const App = () => { const list = [...Array(8).keys()]; const inputRef = useRef([]); const handler = idx => e => { const next = inputRef.current[idx + 1]; if (next) { next.focus() } }; return ( <div className="App"> <div className="input_boxes"> {list.map(x => ( <div> <input key={x} ref={el => inputRef.current[x] = el} onChange={handler(x)} type="number" className="otp_box" /> </div> ))} </div> </div> ); } ReactDOM.render(<App />, document.getElementById("root")); <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

使用ref的数组 正如上面的帖子所说,不建议这样做,因为官方指南(以及内部棉绒检查)不允许它通过。 不要在循环、条件或嵌套函数中调用hook。相反,总是在React函数的顶层使用Hooks。通过遵循此规则,您可以确保在每次组件呈现时以相同的顺序调用hook。 然而,由于这不是我们目前的情况,下面的演示仍然可以工作,只是不推荐。

const inputRef = list.map(x => useRef(null));

inputRef[idx].current.focus();

<input
  ref={inputRef[idx]}
/>

const {useRef} = React; const App = () => { const list = [...Array(8).keys()]; const inputRef = list.map(x => useRef(null)); const handler = idx => () => { const next = inputRef[idx + 1]; if (next) { next.current.focus(); } }; return ( <div className="App"> <div className="input_boxes"> {list.map(x => ( <div> <input key={x} ref={inputRef[x]} onChange={handler(x)} type="number" className="otp_box" /> </div> ))} </div> </div> ); } ReactDOM.render(<App />, document.getElementById("root")); <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

您可以使用父元素来获得一堆子元素。

在我的情况下,我试图得到一串输入在我的表单元素,然后我得到的形式元素,并使用它来处理所有的输入。

就像这样:

function Foo() {
    const fields = useRef<HTMLFormElement>(null);

    function handlePopUp(e) {
      e.preventDefault();
    
      Array.from(fields.current)
        .forEach((input: HTMLInputElement | HTMLTextAreaElement) => {
          input.value = '';
        });
    }

    return (
    <form onSubmit={(e) => handlePopUp(e)} ref={fields}>

      <input
        placeholder="Nome"
        required
        id="name"
        type="text"
        name="name"
      />
      <input
        placeholder="E-mail"
        required
        id="email"
        type="email"
        name="email"
      />
      <input
        placeholder="Assunto"
        required
        id="subject"
        type="text"
        name="subject"
      />
      <textarea
        cols={120}
        placeholder="Descrição"
        required
        id="description"
        name="description"
      />

      <button type="submit" disabled={state.submitting}>enviar</button>
    </form>  
    );
}