【问题标题】:Is it possible to use React Hooks in class component by using HOC(Higher Order Component)?是否可以通过使用 HOC(高阶组件)在类组件中使用 React Hooks?
【发布时间】:2022-01-19 05:22:33
【问题描述】:

可以在类组件中使用功能组件吗?我将调用从类组件中的功能组件中提取的函数。但它给出了如下错误。

未处理的拒绝(错误):无效的挂钩调用。钩子只能在函数组件的主体内部调用。这可能是由于以下原因之一

所以我尝试在功能组件中调用它,但是即使在功能组件中,我也遇到了与在类组件中调用它时相同的错误。

功能组件

import React, { useEffect } from 'react';
import { UseWalletProvider, useWallet } from 'use-wallet';
import { providers } from 'ethers';

export function App() {
  useEffect(() => {
    async function GetBlockId() {
      const wallet = useWallet();
      console.log(wallet); // =====> This is not displaying.
      const { ethereum, connect } = wallet;
      const ethersProvider = new providers.Web3Provider(ethereum);
      const { blockNumber } = await ethersProvider.getTransaction(hash);
      console.log(blockNumber);
    };
    GetBlockId()
  }, []);

  return <div>
    <h1>{wallet}</h1>
  </div>
}

类组件

import React, { Component } from 'react'
import { GetBlockId } from './util';   // =====>> I hope to get result from here.
import { hash } from './hash'

export default class App extends Component {
    constructor(props) {
        super(props)
    }
    componentDidMount(): void {
        const blockNumber: any = GetBlockId(hash);
        console.log(blockNumber);
    }
    render() {
        return (
            <div>
                <h1>test</h1>
            </div>
        )
    }
}

util.tsx

import React, { useEffect } from 'react';
import { UseWalletProvider, useWallet } from 'use-wallet';
import { providers } from 'ethers';
// import { Container } from './styles';

export function GetBlockId() {
  useEffect(() => {
    async function GetBlockId() {
      const wallet = useWallet();
      const { ethereum, connect } = wallet;
      const ethersProvider = new providers.Web3Provider(ethereum);
      const { blockNumber } = await ethersProvider.getTransaction(hash);
      return blockNumber;
    };
    GetBlockId()
  }, []);
}

所以最后我希望在类组件中使用“use-wallet”包。那可能吗?如果是,如何在类组件中使用 useWallet 钩子?

【问题讨论】:

  • 将问题重命名为:如何使用组件将在反应功能组件中更新。即您需要使用组件将使用您尝试使用的任何钩子进行更新。而且好像反应不是很像。
  • 看起来你对 React 和 hooks 的工作方式有一个根本性的误解。我建议你回去阅读文档。 reactjs.org/docs/hooks-rules.html
  • 我回应 @Benjamin 评论。从文档中可以清楚地看出,OP 的要求是被禁止的。

标签: reactjs typescript react-hooks wallet-connect


【解决方案1】:

React hooks 只兼容 React 函数组件,根本不能在类组件中使用。您第一次尝试的问题是您试图在回调中调用 React 挂钩,这违反了挂钩规则之一。

Rules of Hooks

仅在顶层调用 Hooks

不要在循环、条件或嵌套函数中调用 Hooks。 相反,总是在你的 React 函数的顶层使用 Hooks, 在任何提前返回之前。通过遵循此规则,您可以确保 每次渲染组件时,都会以相同的顺序调用挂钩。 这就是让 React 正确保存 Hooks 状态的原因 在多个 useState 和 useEffect 调用之间。 (如果你好奇, 我们将在下面深入解释这一点。)

仅从 React 函数调用 Hooks

不要从常规 JavaScript 函数中调用 Hooks。相反,您可以:

  • ✅ 从 React 函数组件调用 Hooks。
  • ✅ 从自定义 Hooks 调用 Hooks(我们将在下一页了解它们)。

通过遵循此规则,您可以确保所有有状态的逻辑在一个 组件从其源代码中清晰可见。

您的代码在传递给useEffect 挂钩的回调函数中调用useWallet。请注意,这与自定义 Hook 调用另一个 Hook 不同。

useWallet 挂钩调用移出到函数组件主体中。这将关闭渲染范围中的wallet 值,并且将在useEffect 挂钩回调中可用/可访问。我假设您仍然只希望/需要在组件安装时运行一次 useEffect 挂钩,所以我将不理会这方面。

import React, { useEffect } from 'react';
import { UseWalletProvider, useWallet } from 'use-wallet';
import { providers } from 'ethers';

export function App() {
  const wallet = useWallet();

  useEffect(() => {
    console.log(wallet);
    const { ethereum, connect } = wallet;

    async function GetBlockId() {          
      const ethersProvider = new providers.Web3Provider(ethereum);
      const { blockNumber } = await ethersProvider.getTransaction(hash);
      console.log(blockNumber);
    };

    GetBlockId();
  }, []);

  return (
    <div>
      <h1>{wallet}</h1>
    </div>
  );
}

更新

要将useWallet 钩子与类组件一起使用,我建议创建一个可以使用它并将wallet 值作为道具传递的高阶组件。

例子:

const withWallet = Component => props => {
  const wallet = useWallet();
  return <Component {...props} wallet={wallet} />;
};

装饰类组件并通过this.props.wallet访问

class App extends Component {
  constructor(props) {
    super(props)
  }
  componentDidMount(): void {
    const { ethereum, connect } = this.props.wallet;

    ...
  }
  render() {
    return (
      ...
    );
  }
}

export default withWallet(App);

【讨论】:

  • 感谢您的友好回答。好吧,我的最终目的是希望从这里得到结果。 => const wallet = useWallet()。我希望在类组件中使用这个wallet。那可能吗?我编写了功能组件示例只是为了测试。我需要使用类组件。我想知道如何在类组件中使用useWallet()
  • @SatelBill 你不能在类组件中使用 React 钩子,但你可以创建一个 withWallet 高阶组件,它可以使用钩子并将 wallet 值作为道具传递给你的类零件。如果这听起来对您有用并且您感兴趣,我可以更新我的答案。
  • 如果您可以使用 HOC 重新发布您的答案,我将不胜感激。谢谢。
  • @SatelBill 当然可以。答案已更新。
【解决方案2】:

你不能在类组件中调用 react hook。

根据 ReactJS Doc 可以组合功能。

你不能在类组件中使用 Hooks,但你绝对可以将类和函数组件与 Hooks 混合在一个树中。组件是使用 Hooks 的类还是函数是该组件的实现细节。从长远来看,我们希望 Hooks 成为人们编写 React 组件的主要方式。

【讨论】:

    【解决方案3】:

    GetBlockId 不是 React 函数式组件。没有返回方法;因此它会抛出一个错误,说你不能在非功能组件中使用钩子。将此函数更改为功能组件(通过返回 JSX 组件),它应该可以工作。

    请注意,您的 getBlockId 函数是递归的并且会失败。

    根据文档。在您的类组件(父组件)中。您将需要使用 UseWalletProvider 并在您的功能组件中使用钩子。

    这是一个示例(未经测试),希望它能让您顺利上路。

    import React, {useState} from 'react';
    import { useWallet, UseWalletProvider } from 'use-wallet';
    import { ethers } from 'ethers';
    import { hash } from './hash'
    function App() {
      const wallet = useWallet()
      const blockNumber = wallet.getBlockNumber()
      const [blockId, setBlockId] = useState('')
      useEffect(()=>{
        const getBlockId = async() => {
          const { ethereum, connect } = wallet;
          const ethersProvider = new ethers.providers.Web3Provider(ethereum);
          return await ethersProvider.getTransaction(hash);
        }
        setBlockId(getBlockId());
      },[]);
      //Note that now blockId contains the blockId that you get.
      //You can use it with child components etc.
      return (
        <div>
            <p>{blockId}</p>
        </div>
      );
    }
    
    function Index() {
      return (
        <UseWalletProvider
        chainId={1}
        connectors={{
          // This is how connectors get configured
          portis: { dAppId: 'my-dapp-id-123-xyz' },
        }}
      >
        <App />
      </UseWalletProvider>
      );
    }
    

    【讨论】:

      【解决方案4】:

      试试下面的代码,你最好不要在函数组件中使用useXXX,

      export function App() {
        const wallet = useWallet();
        const getBlockId = useCallback(() => {
            console.log(wallet);
            const { ethereum, connect } = wallet;
            const ethersProvider = new providers.Web3Provider(ethereum);
            const { blockNumber } = await ethersProvider.getTransaction(hash);
            console.log(blockNumber);
        }, [wallet]);
        useEffect(() => {
          getBlockId()
        }, []);
      
        return <div>
          <h1>{wallet}</h1>
        </div>
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-19
        • 2020-02-06
        • 2018-09-14
        • 2019-01-07
        • 2019-01-29
        相关资源
        最近更新 更多