是否可以通过使用 HOC(高阶组件)在类组件中使用 React Hooks?

Posted

技术标签:

【中文标题】是否可以通过使用 HOC(高阶组件)在类组件中使用 React Hooks?【英文标题】:Is it possible to use React Hooks in class component by using HOC(Higher Order Component)? 【发布时间】: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 的要求是被禁止的。 【参考方案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>

【讨论】:

以上是关于是否可以通过使用 HOC(高阶组件)在类组件中使用 React Hooks?的主要内容,如果未能解决你的问题,请参考以下文章

[react] 使用高阶组件(HOC)实现一个loading组件

高阶函数HOF和高阶组件HOC(Higher Order Func/Comp)

高阶函数HOF和高阶组件HOC(Higher Order Func/Comp)

HOC — react高阶组件

react-高阶组件-Hoc

React HOC(高阶组件)