【问题标题】:useRef for element in loop in react反应中循环中的元素的useRef
【发布时间】:2021-03-28 16:46:29
【问题描述】:

使用 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>

然后将 refs 传递给动画函数,一切正常; 现在事情发生了一些变化,我使用地图创建了列表项,我不再能够正确地引用元素; 我试过类似的东西:

{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)}
    

    这不起作用 我可以尝试任何其他解决方案吗?

  • 【问题讨论】:

    • 您可能想要React.useRef() 而不是React.createRef()createRef() 仅用于 React 类组件。
    • 但是,在循环内调用 any 钩子在技术上违反了“钩子规则”。如果您绝对肯定地知道“菜单”数组的长度不会改变,您可以这样做,但要小心。
    • @jered React.createRef 可以在任何你想要/需要创建反应引用的地方使用,我所知道的没有规则说它们只能用于基于类的组件中。如果是这样的话,那么 React docs 肯定会说明这一点。

    标签: reactjs use-ref


    【解决方案1】:

    问题

    这不会起作用,因为当 menu 被映射时,每次渲染都会创建新的 react refs。

    解决方案

    使用 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>
      );
    })}
    

    【讨论】:

    • 抱怨 ??是&&还是|| ?
    • @RaulH 从技术上讲,两者都不是,但在这个用例中,Nullish Coalescing Operator (??) 更像是逻辑 OR (||)。这里的要点是,如果左侧 (LHS) 特别是 nullundefined 右侧 (RHS i>) 被返回。对于||,如果 LHS 是 any 错误值,则返回 RHS。
    【解决方案2】:

    我的是 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>
        )
     })
    }
    

    这是一个可行的演示,希望对您有所帮助。 ^_^

    
    
    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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-07
      • 1970-01-01
      • 2020-12-06
      • 1970-01-01
      • 1970-01-01
      • 2017-10-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多