Как использовать React Hooks в компоненте класса?
Могу ли я использовать функциональные компоненты в компонентах класса? Я собираюсь вызвать функцию, которая извлекается из функционального компонента в компоненте класса. Но это дает следующие ошибки.
Необработанное отклонение (ошибка): недопустимый вызов ловушки. Хуки могут быть вызваны только внутри тела функционального компонента. Это могло произойти по одной из следующих причин.
Поэтому я попытался вызвать его в функциональном компоненте, но даже в функциональном компоненте я получил ту же ошибку, что и при вызове его в компоненте класса.
Функциональная составляющая
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 в компоненте класса?
4 ответа
Хуки React совместимы только с функциональными компонентами React, их вообще нельзя использовать в компонентах класса. Проблема с вашей первой попыткой заключается в том, что вы пытаетесь вызвать хук React в обратном вызове, что нарушает одно из правил хуков.
Вызывать хуки только на верхнем уровне
Не вызывайте хуки внутри циклов, условий или вложенных функций. Вместо этого всегда используйте хуки на верхнем уровне вашей функции React, до любого раннего возврата. Следуя этому правилу, вы гарантируете, что хуки вызываются в одном и том же порядке каждый раз при рендеринге компонента. Именно это позволяет React правильно сохранять состояние хуков между несколькими вызовами useState и useEffect. (Если вам интересно, мы подробно объясним это ниже.)
Вызывать хуки только из функций React
Не вызывайте хуки из обычных функций JavaScript. Вместо этого вы можете:
- ✅ Вызов хуков из функциональных компонентов React.
- ✅ Вызов хуков из пользовательских хуков (мы узнаем о них на следующей странице).
Следуя этому правилу, вы гарантируете, что вся логика с отслеживанием состояния в компоненте четко видна из его исходного кода.
Ваш код вызывает функцию обратного вызова, переданную в хук. Обратите внимание, что это не то же самое, что пользовательский хук, вызывающий другой хук.
Переместите вызов ловушки в тело функционального компонента. Это закроет значение в области рендеринга и будет доступно/доступно в обратном вызове ловушки. Я предполагаю, что вы все еще хотите / нуждаетесь в
useEffect
hook для запуска один раз при монтировании компонента, поэтому я оставляю этот аспект в покое.
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);
Вы не можете вызвать хук реакции внутри компонента класса.
Согласно ReactJS Doc , вы можете комбинировать функциональность.
Вы не можете использовать хуки внутри компонента класса, но вы определенно можете смешивать классы и функциональные компоненты с хуками в одном дереве. Является ли компонент классом или функцией, использующей хуки, — это деталь реализации этого компонента. В долгосрочной перспективе мы ожидаем, что хуки станут основным способом написания компонентов React.
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>
);
}
попробуйте следующий код, вам лучше не использовать 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>
}