【问题标题】:How to add "refs" dynamically with react hooks?如何使用反应钩子动态添加“refs”?
【发布时间】:2019-09-23 12:46:24
【问题描述】:

所以我有一个数据数组,我正在生成一个包含该数据的组件列表。我想在每个生成的元素上都有一个 ref 来计算高度。 我知道如何使用 Class 组件来实现,但我想使用 React Hooks 来实现。

这是一个解释我想要做什么的例子:

import React, {useState, useCallback} from 'react'
const data = [
  {
    text: 'test1'
  },
  {
    text: 'test2'
  }
]
const Component = () => {
  const [height, setHeight] = useState(0);
  const measuredRef = useCallback(node => {
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height);
    }
  }, []);

  return (
    <div>
      {
        data.map((item, index) => 
          <div ref={measuredRef} key={index}>
            {item.text}
          </div>
        )
      }
    </div>
  )
}

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    我创建了一个很小的 ​​npm 包,它公开了一个 React Hook 来动态处理设置和获取引用,因为我经常遇到同样的问题。

    npm i use-dynamic-refs
    

    这是一个简单的例子。

    import React, { useEffect } from 'react';
    import useDynamicRefs from 'use-dynamic-refs';
    
    const Example = () =>  {
      const foo = ['random_id_1', 'random_id_2'];
      const [getRef, setRef] =  useDynamicRefs();
    
      useEffect(() => {
        // Get ref for specific ID 
        const id = getRef('random_id_1');
        console.log(id)
      }, [])
    
        return ( 
          <>
            {/* Simple set ref. */}
            <span ref={setRef('random_id_3')}></span>
    
             {/*  Set refs dynamically in Array.map() */}
            { foo.map( eachId => (
              <div key={eachId} ref={setRef(eachId)}>Hello {eachId}</div>))}
          </>
        )
    }
    
    export default Example;
    

    【讨论】:

    • 你救了我。谢谢!
    • 干得好。UPVOTED。
    • 它返回我为空。每当我尝试做 getRef
    【解决方案2】:

    不确定我是否完全理解你的意图,但我认为你想要这样的东西:

    const {
      useState,
      useRef,
      createRef,
      useEffect
    } = React;
    
    const data = [
      {
        text: "test1"
      },
      {
        text: "test2"
      }
    ];
    
    const Component = () => {
      const [heights, setHeights] = useState([]);
      const elementsRef = useRef(data.map(() => createRef()));
    
      useEffect(() => {
        const nextHeights = elementsRef.current.map(
          ref => ref.current.getBoundingClientRect().height
        );
        setHeights(nextHeights);
      }, []);
    
      return (
        <div>
          {data.map((item, index) => (
            <div ref={elementsRef.current[index]} key={index} className={`item item-${index}`}>
              {`${item.text} - height(${heights[index]})`}
            </div>
          ))}
        </div>
      );
    };
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<Component />, rootElement);
    .item {
      box-sizing: border-box;
      display: flex;
      align-items: center;
      justify-content: center;
      border: 1px solid #ccc;
    }
    
    .item-0 {
      height: 25px;
    }
    
    .item-1 {
      height: 50px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
    <div id="root"/>

    【讨论】:

    • 这或多或少是我需要的。谢谢!
    • 如果你使用钩子,你应该使用 useRef 而不是 createRef。
    • 如果数据动态变化(添加、删除)则不起作用
    • @akshay您可以使用地图对象而不是数组
    • @giorgim 如果数据是动态的,则将其放入状态
    【解决方案3】:

    您必须为每个项目使用一组单独的钩子,这意味着您必须为项目定义一个组件(否则您在循环中使用钩子,这是不允许的)。

    const Item = ({ text }) => {
        const ref = useRef()
        const [ height, setHeight ] = useState()
        useLayoutEffect(() => {
            setHeight( ref.current.getBoundingClientRect().height )
        }, [])
        return <div ref={ref}>{text}</div>
    }
    

    【讨论】:

    • 我认为可能有一种我看不到的方式......我对钩子很陌生。我想我将不得不使用一个类组件!谢谢你的回答!
    • 对不起,如果不清楚,你可以用钩子来做,只需在data.map中渲染&lt;Item&gt;
    猜你喜欢
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    • 1970-01-01
    • 2020-03-19
    相关资源
    最近更新 更多