2

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

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

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

功能组件

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

实用程序.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挂钩?

4

4 回答 4

4

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

钩子规则

仅在顶层调用 Hooks

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

仅从 React 函数调用 Hooks

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

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

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

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

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);
于 2021-12-16T07:11:27.303 回答
3

您不能在类组件中调用反应钩子。

根据 ReactJS Doc,您可以组合功能。

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

于 2021-12-16T03:54:49.457 回答
-1

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>
  );
}
于 2021-12-16T03:54:35.327 回答
-2

试试下面的代码,你最好不要在函数组件中使用 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>
}
于 2021-12-16T03:57:52.020 回答