Javascript 如何在映射中使用react useRef实现目标DOM

Javascript 如何在映射中使用react useRef实现目标DOM,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我正在寻找一个关于使用reactuseRef()hook获取DOM元素数组的解决方案 例如: const Component = () => { // In `items`, I would like to get an array of DOM element let items = useRef(null); return <ul> {['left', 'right'].map((el, i) => <li key={i}

我正在寻找一个关于使用react
useRef()
hook获取DOM元素数组的解决方案

例如:

const Component = () => 
{

  // In `items`, I would like to get an array of DOM element
  let items = useRef(null);

  return <ul>
    {['left', 'right'].map((el, i) =>
      <li key={i} ref={items} children={el} />
    )}
  </ul>
}
const组件=()=>
{
//在“items”中,我想得到一个DOM元素数组
let items=useRef(null);
返回
    {['left','right'].map((el,i)=>
  • )}
}

我怎样才能做到这一点

如果您提前知道数组的长度(在您的示例中是这样做的),您只需创建一个ref数组,然后根据其索引分配每个ref:

const Component = () => {
  const items = Array.from({length: 2}, a => useRef(null));
  return (
    <ul>
      {['left', 'right'].map((el, i)) => (
        <li key={el} ref={items[i]}>{el}</li>
      )}
    </ul>
  )
}
const组件=()=>{
const items=Array.from({length:2},a=>useRef(null));
返回(
    {['left','right'].map((el,i))=>(
  • {el}
  • )}
) }
useRef
与React的
ref
只是部分相似(只是对象的结构,只有当前的
字段)

useRef
hook的目标是在渲染之间存储一些数据,并且更改这些数据不会触发重新渲染(与
useState
不同)

还有一个温和的提醒:最好避免在循环中初始化钩子,或者在
的情况下初始化钩子。是的

考虑到这一点,我们:

  • 通过
    useRef
  • 我们通过
    createRef()
  • 我们可以使用
    .current
    符号来参考列表

    const Component = () => {
    
      let refs = useRef([React.createRef(), React.createRef()]);
    
      useEffect(() => {
        refs.current[0].current.focus()
      }, []);
    
      return (<ul>
        {['left', 'right'].map((el, i) =>
          <li key={i}><input ref={refs.current[i]} value={el} /></li>
        )}
      </ul>)
    }
    
    也不要在第一次渲染时尝试访问
    refs.current[0].current
    -这将引发错误

    返回(
      {['left','right'].map((el,i)=>
    • {refs.current[i].current.value}
    • //无法读取未定义的属性'value' )}
    所以你要么把它当作

          return (<ul>
            {['left', 'right'].map((el, i) =>
              <li key={i}>
                <input ref={refs.current[i]} value={el} />
                {refs.current[i].current && refs.current[i].current.value}</li> // cannot read property `value` of undefined
            )}
          </ul>)
    
    返回(
      {['left','right'].map((el,i)=>
    • {refs.current[i]。current&&refs.current[i]。current.value}
    • //无法读取未定义的属性'value' )}
    或者在
    useffect
    hook中访问它。原因:
    ref
    s是在元素渲染后绑定的,因此在渲染过程中第一次运行时它还没有初始化。

    我将稍微展开一下。对于性能优化(并避免潜在的奇怪错误),您可能更喜欢使用
    usemo
    而不是
    useRef
    。由于useMemo接受回调作为参数而不是值,
    React.createRef
    将在第一次渲染后仅初始化一次。在回调中,您可以返回
    createRef
    值的数组,并适当地使用该数组

    初始化:

      const refs= useMemo(
        () => Array.from({ length: 3 }).map(() => createRef()),
        []
      );
    
      refs[i+1 % 3].current.focus();
    
    此处的空数组(作为第二个参数)告诉React仅初始化引用一次。如果ref count发生更改,您可能需要将
    [x.length]
    作为“一个deps数组”传递,并动态创建ref:
    array.from({length:x.length}).map(()=>createRef(),

    用法:

      const refs= useMemo(
        () => Array.from({ length: 3 }).map(() => createRef()),
        []
      );
    
      refs[i+1 % 3].current.focus();
    

    获取父引用并操纵子引用

    const组件=()=>{
    const ulRef=useRef(null);
    useffect(()=>{
    ulRef.current.children[0].focus();
    }, []);
    返回(
    
      {['left','right'].map((el,i)=>(
    • ))}
    );
    };您可以将每个映射项分离到组件,而不是使用引用数组或类似的东西。将它们分开时,可以单独使用
    useRef
    s:

    const数据=[
    {id:0,名称:“John”},
    {id:1,名称:“Doe”}
    ];
    //使用引用数组或类似的方法:
    函数组件(){
    const items=useRef(数组(DATA.length).fill(createRef());
    返回(
    
      {DATA.map((项目,i)=>(
    • {item.name}
    • ))}
    ); } //将每个映射项分离到组件: 函数组件(){ 返回(
      {DATA.map((项目,i)=>( ))}
    ); } 函数MapItemComponent({data}){ const itemRef=useRef(); return
  • {data.name}

  • }
    我遇到了这样一个问题,读了Joer的答案,意识到您可以使用索引动态设置querySelector类,并只为整个父类设置一个引用。抱歉代码太多,但希望这对某人有所帮助:

    import React, { useRef, useState } from 'react';
    import { connectToDatabase } from "../util/mongodb";
    
    export default function Top({ posts }) {
      //const [count, setCount] = useState(1);
      const wrapperRef = useRef(null);
    
    
      const copyToClipboard = (index, areaNumber) => {
        // 
        // HERE I AM USING A DYNAMIC CLASS FOR THE WRAPPER REF 
        // AND DYNAMIC QUERY SELECTOR, THEREBY ONLY NEEDING ONE REF ON THE TOP PARENT
        const onePost = wrapperRef.current.querySelector(`.index_${index}`)
        const oneLang = onePost.querySelectorAll('textarea')[areaNumber];
        oneLang.select();
        document.execCommand('copy');
      };
    
      var allPosts = posts.map((post, index) => {
    
        var formattedDate = post.date.replace(/T/, ' \xa0\xa0\xa0').split(".")[0]
        var englishHtml = post.en1 + post.en2 + post.en3 + post.en4 + post.en5;
        var frenchHtml = post.fr1 + post.fr2 + post.fr3 + post.fr4 + post.fr5;
        var germanHtml = post.de1 + post.de2 + post.de3 + post.de4 + post.de5;
    
        return (
          <div className={post.title} key={post._id}>
            <h2>{formattedDate}</h2>
            <h2>{index}</h2>
    
            <div className={"wrapper index_" + index}>
              <div className="one en">
                <h3>Eng</h3>
                <button onClick={() => {copyToClipboard(index, 0)}}>COPY</button>
                <textarea value={englishHtml} readOnly></textarea>
              </div>
    
              <div className="one fr">
                <h3>Fr</h3>
                <button onClick={() => {copyToClipboard(index, 1)}}>COPY</button> 
                <textarea value={frenchHtml} readOnly></textarea>
              </div>
    
              <div className="one de">
                <h3>De</h3>
                <button onClick={() => {copyToClipboard(index, 2)}}>COPY</button>
                <textarea value={germanHtml} readOnly></textarea>
              </div>
            </div>
    
          </div>
        )
      })
    
      return (
        <div ref={wrapperRef}>
          <h1>Latest delivery pages </h1>
          <ul>
            {allPosts}
          </ul>
    
          <style jsx global>{`
    
            body{
              margin: 0;
              padding: 0;
            }
            h1{
              padding-left: 40px;
              color: grey;
              font-family: system-ui;
              font-variant: all-small-caps;
            }
            .one,.one textarea {
              font-size: 5px;
              height: 200px;
              width: 300px;
              max-width:  350px;
              list-style-type:none;
              padding-inline-start: 0px;
              margin-right: 50px;
              margin-bottom: 150px;
              
            }
    
            h2{
              font-family: system-ui;
              font-variant: all-small-caps;
            }
            .one h3 {
              font-size: 25px;
              margin-top: 0;
              margin-bottom: 10px;
              font-family: system-ui;
            }
    
            .one button{
              width: 300px;
              height: 40px;
              margin-bottom: 10px;
            }
    
            @media screen and (min-width: 768px){
              .wrapper{
                display: flex;
                flex-direction: row;
              }
            }
    
          `}</style>
        </div>
      );
    }
    
    import React,{useRef,useState}来自“React”;
    从“./util/mongodb”导入{connectToDatabase};
    导出默认函数Top({posts}){
    //const[count,setCount]=useState(1);
    const wrapperRef=useRef(null);
    const copyToClipboard=(索引,区域编号)=>{
    // 
    //这里,我使用一个动态类作为包装器REF
    //和动态查询选择器,因此只需要顶部父项上的一个REF
    const onePost=wrapperRef.current.querySelector(`.index${index}`)
    const oneLang=onePost.querySelectorAll('textarea')[areaNumber];
    oneLang.select();
    document.execCommand('copy');
    };
    var allPosts=posts.map((post,index)=>{
    var formattedDate=post.date.replace(/T/,'\xa0\xa0\xa0')。拆分(“.”[0]
    var englishHtml=post.en1+post.en2+post.en3+post.en4+post.en5;
    var frenchHtml=post.fr1+post.fr2+post.fr3+post.fr4+post.fr5;
    var germanHtml=post.de1+post.de2+post.de3+post.de4+post.de5;
    返回(
    {formattedDate}
    {index}
    英格
    {copyToClipboard(索引,0)}>复制
    Fr
    {copyToClipboard(索引,1)}>复制
    扩散系数
    {copyToClipboard(索引,2)}>复制
    )
    })
    返回(
    最新交付页面
    
      {allPosts}