【问题标题】:How can I expose functions to the ref using functional components?如何使用功能组件向 ref 公开功能?
【发布时间】:2019-12-11 17:04:28
【问题描述】:

我想创建一个组件,比如说一个日历功能组件,它可以公开一些功能,如nextMonthprevMonth 等。这样使用它的父组件就可以使用ref 访问这些功能。使用类组件很容易,因为实例方法会自动公开。如何使用功能组件实现相同的功能?我知道我可以为此使用渲染道具模式。但是还有其他解决方案吗?

粗略的例子:

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

【问题讨论】:

  • 试试createRef而不是useRef

标签: reactjs react-native


【解决方案1】:

你可以使用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>
  )
}

【讨论】:

  • 是的!谢谢
【解决方案2】:

在功能组件中,您可以使用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>
    </>
  );
}

这项工作:

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>
    </>
  );
}

【讨论】:

    【解决方案3】:

    虽然@Matthieu Libeer 的答案是正确的,这应该对您有用,但您仍然可以考虑以 React 方式进行。现在您的Calendaruncontrolled component 相同。通过 ref 访问它的内部状态(以及基于类的组件的方法),你就输了:

    1. 能够进行预输入验证(例如,放弃每月第二个星期日的选择)
    2. 用不同的组件轻松替换它

    一旦它成为完全可控的组件 + 与 props 名称中的原生控件保持一致,你会得到类似的东西

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

    这样可以很容易地用兼容的组件替换它(比如&lt;input type="date" /&gt;),并且验证更容易。你可能仍然需要 forwardRef.focus() 说命令,但其他方面的痛苦会减少。

    【讨论】:

    • 我完全理解这一点。实际上,Calendar 是一个假设的例子。在某些情况下,公开函数可能会有所帮助。我只是在寻找一个优雅的解决方案。到目前为止,我没有找到比渲染道具模式更好的解决方案。
    【解决方案4】:

    尽管这不是真正的 React 方式,但您可以这样做。 诀窍是在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>
      )
    }
    

    【讨论】:

    • 正如解释:我们需要.forwardRef,因为ref(以及key btw)不是普通的道具,所以它不会被列在Calendar的道具中
    • 我也想过,但它似乎不是一个优雅的解决方案。渲染道具会更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多