Can I use the functional components in class components? I am going to call a function that is extracted from a functional component in class component. But it is giving errors like the following.
Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons
So I tried to call it in the functional component but even in the functional component, I got the same error as when I call it in class component.
Functional component
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>
}
Class component
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()
}, []);
}
So finally I hope to use "use-wallet" package in the class component. Is that possible? If yes, how to use useWallet hook in the class component?
React hooks are only compatible with React function components, they can't be used in class components at all. The issue with your first attempt is that you are trying to call a React hook in a callback, which breaks one of the Rules of Hooks.
Only Call Hooks at the Top Level
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, we’ll explain this in depth below.)
Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions. Instead, you can:
- ✅ Call Hooks from React function components.
- ✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.
You code is calling useWallet
in a callback function passed to the useEffect
hook. Note that this isn't the same thing as a custom Hook calling another hook.
Move the useWallet
hook call out into the function component body. This will close over the wallet
value in the render scope and will be available/accessible in the useEffect
hook callback. I'm assuming you still only want/need the useEffect
hook to run once when the component mounts, so I'm leaving that aspect alone.
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>
);
}
To use the useWallet
hook with a class component I suggest creating a Higher Order Component that can use it and pass the wallet
value as a prop.
Example:
const withWallet = Component => props => {
const wallet = useWallet();
return <Component {...props} wallet={wallet} />;
};
Decorate the class component and access via 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);