【问题标题】:How can I use hooks in a higher-order function that returns a ReactNative functional component in TypeScript?如何在 TypeScript 中返回 ReactNative 功能组件的高阶函数中使用钩子?
【发布时间】:2021-08-28 13:27:20
【问题描述】:

让我从我试图重构的工作代码开始。我正在尝试抽象按钮创建,无论我尝试什么,我都会收到“只能在功能组件的主体内部调用挂钩”错误。

按照教程,我设置了一个导航栏和一个按钮,可将我从主屏幕转到“详细信息”屏幕。以下是有效的:

const DetailsButton = (): JSX.Element => {
  const navigation = useNavigation();

  return (
    <Button title="Details" onPress={() => navigation.navigate('Details')} />
  );
};

// And then later on...

<Stack.Screen name="Home" 
              component={HomeScreen} 
              options={{ title: 'Master', headerRight: DetailsButton }} />

该按钮将毫无问题地将我带到详细信息屏幕。但后来我开始想我可以将它重构为一种按钮工厂函数:

interface BarButtonProps {
  title: string, 
  destination: string,
};

const BarButton = (buttonProps: BarButtonProps): () => JSX.Element => {
  const navigation = useNavigation();

  return (): JSX.Element => (
    <Button title={buttonProps.title} onPress={() => navigation.navigate(buttonProps.destination)} />
  );
}

const DetailsButton = BarButton({title: 'Details', destination: 'Details'});

我的计划是像以前一样使用DetailsButton 组件。但是,当我尝试运行代码时,我得到了钩子错误。我以为我明白了原因,所以我尝试了以下变体:

const BarButton = (buttonProps: BarButtonProps): () => JSX.Element => {
  return (): JSX.Element => (
    <Button title={buttonProps.title} onPress={() => useNavigation().navigate(buttonProps.destination)} />
  );
}

在我看来,钩子现在似乎在功能组件中。但这只能让我到达主屏幕。当我按下按钮时,我再次收到相同的钩子错误。我一定漏掉了一些微妙的地方。

是否有可能创建一个像这样的高阶函数,一个使用钩子的函数,还是我在尝试一些不可能的事情?如果可能的话,有人可以告诉我什么会起作用吗?谢谢。

【问题讨论】:

    标签: typescript react-native react-hooks


    【解决方案1】:

    你已经很接近了。你的工厂应该是这样的:

    const ButtonFactory = (props: ButtonProps): () => JSX.Element => {
      const { title, destination } = props;
    
      const MyButton = (): JSX.Element => {
        const navigation = useNavigation();
    
        return (
          <Button title={title} onPress={() => navigation.navigate(destination)} />
        );
      }
    
      return MyButton;
    };
    
    export const DetailsButton = ButtonFactory({ title: 'Details', destination: 'Details' })
    

    【讨论】:

    • 谢谢。我不得不将代码中的“Button”重命名为“MyButton”之类的名称才能使其真正起作用。该应用程序仍会运行,但运行时的按钮将失效。按下它并没有发生任何事情。我的编辑器显示:“类型'内在属性'上不存在属性'标题'。”我相信按钮类和“按钮”变量之间存在冲突。我将对您的代码进行编辑并将其标记为答案。如果我的编辑有误,请告诉我,再次感谢。
    • 感谢您的编辑。我从沙箱中转移了代码,忘记更改一些名称。
    【解决方案2】:

    当前设置的问题是 useNavigation 钩子在您调用 BarButton({title: 'Details', destination: 'Details'}) 的位置执行,而不是在您调用 &lt;DetailsButton/&gt; 的位置。

    BarButton函数被调用时,你调用useNavigation然后返回一个函数组件。这违反了钩子的规则。您需要移动 useNavigation 挂钩,以便在创建的组件内部调用它。

    只需重新排列行的顺序:

    const BarButton = (buttonProps: BarButtonProps): () => JSX.Element => {
        return (): JSX.Element => {
            const navigation = useNavigation();
            return (
                <Button title={buttonProps.title} onPress={() => navigation.navigate(buttonProps.destination)} />
            );
        }
    }
    

    就我个人而言,我会用小写的名称写工厂,以表明这是一个创建组件而不是组件本身的函数。

    import * as React from 'react';
    import { Button } from 'react-native';
    import { useNavigation } from '@react-navigation/native';
    
    interface BarButtonProps {
      title: string;
      destination: string;
    }
    
    const createBarButton = ({title, destination}: BarButtonProps) => (): JSX.Element => {
      const navigation = useNavigation();
      return (
        <Button
          title={title}
          onPress={() => navigation.navigate(destination)}
        />
      );
    };
    
    const DetailsButton = createBarButton({
      title: 'Details',
      destination: 'Details',
    });
    

    【讨论】:

    • 非常感谢您详细的 cmets 和紧凑的代码。你帮助我理解了我哪里出错了。 (另一个人首先回答并解决了我的问题,因此我将他标记为接受的答案。)我看到的是我的眼睛迷失了,我正在返回一个实际上没有返回任何东西的函数。 (我错过了一个返回声明,当我将我的 sn-p 与你的比较时,我看到了。)再次感谢!
    • 您的回报很好,因为=&gt; ( 语法是隐式/自动回报。如果它有括号=&gt; {那么没有自动返回,所以你必须写=&gt; { return (Docs。我必须添加一个显式的return,因为我需要添加方括号才能在返回的组件中包含挂钩调用。
    猜你喜欢
    • 2020-08-22
    • 2018-05-23
    • 2021-02-17
    • 2021-04-14
    • 2021-10-17
    • 1970-01-01
    • 2021-06-26
    • 2019-10-10
    • 2020-03-12
    相关资源
    最近更新 更多