反应-转发多个引用

时间:2018-11-30 17:04:23

标签: reactjs

我有一个SideNav组件,其中包含动态创建的链接,这些链接需要滚动到相邻html表(InfoTable)中的相应头。我尝试了多种不同的方法来完成此操作,但无济于事。

export default class Parent extends Component {
  state = {
    categories: [],
  }

  scrollToHeader = (tableRefString) => {
    // Function to pass to SideNav to access refs in InfoTable
    this[tableRefString].scrollIntoView({ block: 'start' });
  }

  render() {
    return (
      <div>
        <SideNav
          categories={this.state.categories}
          scrollToHeader={this.scrollToHeader} />
        <InfoTable
          categories={this.state.categories} />
      </div>
    );
  }
}

export default class InfoTable extends Component {
  render() {
    return (
      <div>
        <table>
          <tbody>
            {this.props.categories.map(category => (
              <>
                // Forward the ref through InfoTableHeader to be set on the parent DOM node of each InfoTableHeader
                <InfoTableHeader />
                {category.inputs.map(input => <InfoTableRow />)}
              </>
            ))}
          </tbody>
        </table>
      </div>
    );
  }
}

为了单击SideNav上的链接并滚动到InfoTable上的相应标题,我相信我需要转发根据我的类别数组中的名称在Parent上动态创建的引用,并将这些引用设置为DOM节点InfoTable中的每个标头。从那里,我将一个函数传递给SideNav,该函数可以访问Parent中的引用以滚动到标题。

  • 如何将多个引用一次转发到我的InfoTable组件?
  • 是否有一种更干净的方法来完成我想做的事情?我已经研究过React.findDOMNode(),但是引用似乎是更好的选择。

3 个答案:

答案 0 :(得分:21)

我知道有一个已经被接受的答案,尽管我发现@ nicholas-haley的解决方案是可以接受的。 我认为一种更好的解决方法是使用内置的useImperativeHandle挂钩。

重要提示:React Hooks Api自

开始可用
  • react@16.8.0及更高版本
  • react-native@0.59.0及更高版本

The React hooks API Docs状态:

useImperativeHandle自定义使用ref时公开给父组件的实例值。与往常一样,在大多数情况下应避免使用ref的命令性代码。 useImperativeHandle应该与`forwardRef

一起使用

此注释后面是以下示例:

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

因此,我认为,更干净的解决方案是通过useImperativeHandle钩子委派所需的引用。

这样,就不需要特殊的ref语法,并且组件可以简单地返回特定类型的FatherRef。示例:

// LabelInput.js
function LabelInput(props, ref) {
    const labelRef = useRef();
    const inputRef = useRef();

    useImperativeHandle(ref, () => ({
      focus: () => {
       inputRef.current.focus();
      },
      get input() {
          return inputRef.current;
      },
      get label() {
          return labelRef.current;
      },
      // ... whatever else one may need
    }));
    return (
      <div>
        <label ref={labelRef} ... />
        <input ref={inputRef} ... />;
      </div>
    )
}
LabelInput = forwardRef(LabelInput);

function MyScreen() {
   const labelInputRef = useRef();

   const onClick = useCallback(
    () => {
//       labelInputRef.current.focus(); // works
//       labelInputRef.current.input.focus(); // works
// ... etc
    },
    []
   );

   return (
     ...
      <LabelInput ref={labelInputRef} ... />
     ....
   )
}

答案 1 :(得分:4)

我遇到过类似的情况,我需要将多个引用转发给我的Parent组件的子代。

我仍然没有找到一个好的解决方案,但是您可以尝试将ref作为对象传递,并在forwardRef回调中进行破坏:

// Parent
ref={{
    ref1: this.ref1,
    ref2: this.ref2
}}

// Child
export default React.forwardRef((props, ref) => {
  const { ref1, ref2 } = ref;

  return (
    <Child
      {...props}
      ref1={ref1}
      ref2={ref2}
    />
  );
});

我不太喜欢此处的命名(我更喜欢将ref称为refs),但这在您遇到麻烦时可以使用。

答案 2 :(得分:1)

我实际上只是从react-hook-form中挑选出来的,但是他们提供了一种共享引用并立即分配多个引用的好方法:

<input name="firstName" ref={(e) => {
  register(e) // use ref for register function
  firstNameRef.current = e // and you can still assign to ref
}} />