如何使用 TypeScript 在 React Native 中将 forwardRef 与 FunctionComponent 一起使用

IT技术 javascript reactjs typescript react-native react-hooks
2021-03-26 10:03:31

我已经查看了许多文档和示例,但我似乎仍然不太明白如何forwardRef在 React Native 中使用带有 TypeScript 的功能组件。下面是一个示例,其中我创建了MyCustomComponent一个自定义函数,我尝试通过创建引用从父级调用该函数。但是,由于 ref 定义不正确null,我显然收到一条错误消息,告诉我该函数不存在。请帮助我了解如何forwardRef在 React Native 中正确使用提前致谢!

interface MyCustomComponentProps {
  title: string
}

const MyCustomComponent: React.FunctionComponent<MyCustomComponentProps> = React.forwardRef((props, ref) => {
  const coolAlert = () => {
    Alert.alert('Hey!', 'This was called from MyCustomComponent')
  }
  return (
    <View>
      <Text>{props.title}</Text>
    </View>
  )
})

export default function App () {
  const MyCustomComponentRef = useRef()
  return (
    <SafeAreaView>
      <MyCustomComponent ref={MyCustomComponentRef} title='Hello World' />
      <TouchableOpacity
        onPress={() => {
          MyCustomComponentRef.coolAlert()
        }}>
        <Text>Click Me</Text>
      </TouchableOpacity>
    </SafeAreaView>
  )
}
1个回答

转发参考

Refs 可能真的很令人困惑,因为有多种方法可以处理它们,而且人们不知道 ref 对象(React.MutableRefObjectReact.RefObject)和 ref 值之间的区别,该值存储在.currentref 对象属性中。你在这里犯了那个错误,以及一些丢失或不正确的typescript类型。

useRef<T>是一个通用钩子,其中的值T表明将存储什么类型的值。我们需要App说明我们打算用一个coolAlert方法存储一些东西实际上我们稍后会看到我们需要我们的 ref 是不可变的,所以我们将使用它createRef<T>

interface MyRef {
  coolAlert(): void;
}

const MyCustomComponentRef = createRef<MyRef>();

当我们调用 时onPress,我们需要访问 ref 对象的当前值。通过将泛型添加到createRef,typescript已经知道这个值是MyRefundefined我们可以coolAlert使用可选的链接?.运算符进行调用

onPress={() => MyCustomComponentRef.current?.coolAlert()}

现在我们需要做一些工作MyCustomComponent您将类型分配给它是错误的,React.FunctionComponent<MyCustomComponentProps>因为函数组件不具备我们需要的关于 ref 转发的知识。

function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

for 的类型MyCustomComponent应该是来自forwardRef. 但是,我们并不需要这种类型的分配自己,我们只需要通过仿制药T,并PforwardRef函数调用。 T是 refP的类型,是 props 的类型。

const MyCustomComponent = React.forwardRef<MyRef, MyCustomComponentProps>(...

好的,我们摆脱了所有typescript错误耶!除了……等等。它实际上没有做任何事情所有这一切,它仍然不起作用。 我讨厌裁判。参考是坏的。

使用参考

我们将 ref 转发给MyCustomComponent,他现在可以访问转发的 ref 并且可以将其附加到 DOM 组件。但是我们不希望它附加到 DOM 元素,我们希望它附加到MyCustomComponent. 但我们真的不能那样做。

默认情况下,您不能在函数组件上使用 ref 属性,因为它们没有实例[docs]

我们必须使用一个叫做 hook 的钩子useImperativeHandle,它感觉像是一个黑客解决方案,甚至文档都说“不要这样做”。是的,我讨厌裁判。

useImperativeHandle 自定义在使用 ref 时暴露给父组件的实例值。与往常一样,在大多数情况下应该避免使用 refs 的命令式代码。useImperativeHandle 应该与 forwardRef 一起使用。[文档]

我们必须coolAlert通过useImperativeHandle.

useImperativeHandle(ref , () => ({coolAlert}));

现在它真的有效了,终于!

这很好,但不是很好,如果这是有道理的。基本上,react 组件之间的通信是自上而下的,上层组件将回调向下传递给其子组件,子组件通过调用这些回调函数与父组件进行通信。您想要做的是在父级中声明coolAlert 函数并通过props 传递给子级。我不知道您的实际代码是什么,但我认为您这样做是有原因的。但是可能有一种方法可以重新组织它,以便数据自上而下流动。
2021-05-26 10:03:31
一般不鼓励转发引用。它有效,但有点倒退。您会听到术语“反模式”用于描述与其预期用途相反的代码结构。鼓励在可能的情况下通过道具控制孩子。
2021-06-09 10:03:31
哇!非常感谢!顺便说一句,当你说不useImperativeHandle鼓励使用时,我认为只要我使用它就可以forwardRef了?
2021-06-12 10:03:31
您还可以考虑通过 redux 的 react 上下文 API 或直接使用上下文来拥有全局应用程序状态(尽管 react 文档告诉您在不需要时避免使用上下文)。为了寻找完美的代码,我倾向于将所有内容重写一百万次。所以一方面我喜欢“我讨厌这个”,另一方面我喜欢“不要成为我。不要仅仅因为您的应用程序不完美而将其烧毁。” 所以是的,要清楚这不是问题,它只是一种不太理想的设计模式。
2021-06-12 10:03:31
我想创建一个文本编辑器和一个工具栏,用户可以在其中将工具栏作为单独的组件导入。这就是为什么我需要在它们之间建立某种桥梁,以便工具栏将命令传递给文本编辑器。也许编写某种桥接或 api 会是一个更好的解决方案。
2021-06-14 10:03:31