简体   繁体   English

如何使用函数组件向 ref 公开函数?

[英]How can I expose functions to the ref using functional components?

I want to create a component lets say a calendar functional component which can expose some functions like nextMonth , prevMonth , etc. So that the parent component using it can access these functions using the ref .我想创建一个组件,比如说一个日历功能组件,它可以公开一些函数,如nextMonthprevMonth等。这样使用它的父组件就可以使用ref访问这些函数。 It was easy with class components as the instance methods are automatically exposed.使用类组件很容易,因为实例方法是自动公开的。 How do I achieve the same with functional components?如何使用功能组件实现相同的功能? I know I can use render props pattern for this.我知道我可以为此使用渲染道具模式。 But any other solution?但是还有其他解决方案吗?

Rough example:粗略的例子:

function Calendar() {
   const nextMonth = () => {...}
   const prevMonth = () => {...}

   return (
      <div>.....</div>
   )
}


function Parent() {
   const cal = createRef();

   const onNext = () => {
     cal.current.nextMonth();
   }

   return (
     <>
        <Calendar ref={cal} />
        <button onClick={onNext}>Next</button>
     </>
   )
}

You can use useImperativeHandle hook.您可以使用useImperativeHandle钩子。

const Calendar = React.forwardRef((props, ref) => {
  const [month, setMonth] = useState(0)
  const nextMonth = () => { setMonth((prev) => prev + 1) }
  const prevMonth = () => { setMonth((prev) => prev - 1) }

  useImperativeHandle(ref, () => ({
    nextMonth,
    prevMonth
  }));

  return (
    <div>{month}</div>
  )
})

const Parent = () => {
  const cal = useRef()

  const onNext = () => {
    cal.current.nextMonth()
  }

  return (
    <React.Fragment>
      <Calendar ref={cal} />
      <button type="button" onClick={onNext}>Next</button>
    </React.Fragment>
  )
}

Here's how you can do it, even though it's not really the React way.这是你可以做到的方法,即使它不是真正的 React 方式。 The trick is to set the current value of the ref in Calendar .诀窍是在Calendar设置ref的当前值。

const Calendar = React.forwardRef((props, ref) => {
  const [month, setMonth] = useState(0)
  const nextMonth = () => { setMonth((prev) => prev + 1) }
  const prevMonth = () => { setMonth((prev) => prev - 1) }

  ref.current = { nextMonth, prevMonth } // eslint-disable-line

  return (
    <div>{month}</div>
  )
})

const Parent = () => {
  const cal = useRef()

  const onNext = () => {
    cal.current.nextMonth()
  }

  return (
    <React.Fragment>
      <Calendar ref={cal} />
      <button type="button" onClick={onNext}>Next</button>
    </React.Fragment>
  )
}

in functional components you can use forwardRef to pass ref to child component.在功能组件中,您可以使用forwardRef将 ref 传递给子组件。

const Calendar = React.forwardRef((props, ref) => {
  const nextMonth = () => {...};
  const prevMonth = () => {...};

  return <div>.....</div>;
});

function Parent() {
  const cal = useRef(null);

  const onNext = () => {
    cal.current.nextMonth();
  };

  return (
    <>
      <Calendar ref={cal} />
      <button onClick={onNext}>Next</button>
    </>
  );
}

this work:这项工作:

const Calendar = React.forwardRef((props, ref) => {
  const nextMonth = () => console.log("nextMonth");
  const prevMonth = () => console.log("prevMonth");
  ref.current = { nextMonth, prevMonth };
  return <div>.....</div>;
});

function Parent() {
  const cal = useRef(null);

  const onNext = () => {
    cal.current.nextMonth();
  };

  return (
    <>
      <Calendar ref={cal} />
      <button onClick={onNext}>Next</button>
    </>
  );
}

While @Matthieu Libeer answer is correct and this should work to you still consider making that in React way.虽然@Matthieu Libeer 的答案是正确的,这应该对您有用,但您仍然可以考虑以 React 的方式进行。 By now your Calendar is the same uncontrolled component .到目前为止,您的Calendar是相同的不受控制的组件 By accessing it's internal state(and methods in case of class-based components) by ref you are loosing:通过 ref 访问它的内部状态(以及基于类的组件的方法),您将失去:

  1. Ability to make pre-input validation(say, to discard selection for each second Sunday in month)能够进行预输入验证(例如,放弃每月第二个星期日的选择)
  2. Replace it with different component in easy way以简单的方式用不同的组件替换它

Once it would be fully controllable component + keeping consistency with native controls in props' names you would get something like一旦它成为完全可控的组件+与道具名称中的本机控件保持一致,你会得到类似的东西

<Calendar value={currentData} onChange={this.validateAndSetDate} />

This way it can be easily replaced with compatible component(say <input type="date" /> ) as well as validation is much more easier.通过这种方式,它可以很容易地替换为兼容的组件(比如<input type="date" /> )并且验证更容易。 You still might need forwardRef say to .focus() imperatively but there will be less pain with other aspects.你仍然可能需要forwardRef命令性地对.focus()说,但其他方面的痛苦会更少。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在功能组件中使用 ref 访问组件的方法 - How can I access methods of a component using ref in functional component 如何将 `this` 的引用传递给使用功能组件的孩子? - How can I pass a reference of `this` to children using functional components? 如何使用功能组件制作待办事项列表? - How can I make a todo list using functional components? 我可以在功能组件上使用 jest/rtl 模拟 ref 属性吗? - Can I mock a ref property using jest/rtl on a functional component? 如何将基于类的组件中的函数转换为 React 中的函数式组件? - How can I convert functions in class-based component to functional components in React? 如何向全局操作公开值以在组件之间共享? - How can I expose a value to the global actions to share it among components? 使用react-router将带有ref的子组件功能公开给父组件 - Expose child component functions to parent with ref using react-router Typescript ESLint 错误 - 使用 React 功能组件时如何输入子项? - Typescript ESLint Error - How can I type children when using react functional components? 如何访问BaseComponent的ref或任何级别的组件? - How can I access the ref of the BaseComponent or any level of the components? 如何在功能组件中编写处理程序和其他功能 - How to write handlers and other functions in functional components
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM