Calling ApolloClient GraphQl request inside componentDidMount method - reactjs

I am using ApolloClient GraphQl query inside react class to fetch data from server:
import React, {Component} from 'react';
import {useCompanyLogo} from '../../queries/companyLogo';
class Logo extends Component {
constructor() {
super();
this.state = {logo: ""};
}
componentDidMount() {
const {error, loading, data} = useCompanyLogo();
if(loading) return <div>spinner</div>
if(error) return <div>error!</div>
const imageSource = data.companyLogo[0].image.urls[0];
this.setState({logo: imageSource});
}
render() {
return (
<div className="logo-area">
<img src={"http://computer-313:5000" + this.state.logo} alt="Businex-Logo" style={{width:"80px"}} />
</div>
);
}
}
export default Logo;
And the query is as below:
import {useQuery, gql} from "#apollo/client";
var COMPANY_LOGO = gql`
query CompanyLogo {
companyLogo {
image {
urls(first: 1)
}
}
}
`;
export const useCompanyLogo = () => {
const {error, data, loading} = useQuery(COMPANY_LOGO);
console.log(error, data, loading);
return {
error,
data,
loading
}
}
Everything works good when I use function instead of class But when I run this code I get the following error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

According to the React.js documentation you cannot use Hooks inside of Class Components.
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
You can try to use high order components and be able to pass the hooks into your Class Component that way.

Related

Is it possible to use React Hooks in class component by using HOC(Higher Order Component)?

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.
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>
);
}
Update
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);
You can't call react hook inside a class component.
According to ReactJS Doc you can combine the functionality.
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
GetBlockId Is Not a React Functional Component. There is no return method; hence it will throw an error saying that you can't use a hook in a non Functional Component. Change this function to a functional component (via returning a JSX component) and it should work.
NOTE that your getBlockId function is recursive and will fail.
According to the docs. In your class component (parent component). You will want to use the UseWalletProvider and in your functional component use the hook.
Here is an example (untested), hopefully that will get you on your way.
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>
);
}
try the following code, you'd better not using useXXX inside an function which in a functional component,
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>
}

How to include the Match object into a Reacts component class?

I am using react router v5, and trying to get URL parameters by props match object into my react component class. However it is not working! What am I doing wrong here?
When I create my component as a JavaScript function it all works fine, but when I try to create my component as a JavaScript class it doesn't work.
Perhaps I am doing something wrong? How do I pass the Match object in to my class component and then use that to set my component's state?
here code:
import React, { Component } from 'react';
import { Link} from "react-router-dom";
export class JobDetail extends Component {
state = {
// jobsData: [],
}
async componentDidMount() {
const props = this.props.match;
console.log("---props data---",props); // it's showing undefined
}
render() {
return (
<>
test message
</>
)
}
}
export default JobDetail

next.js - getInitialProps does not work in component

Here's my code
import React from 'react'
import fetch from 'isomorphic-unfetch'
import Book from './Book'
function getNum(val) {
val = +val || 0;
return val;
}
class BookList extends React.Component {
static async getInitialProps(ctx) {;
const res = await fetch('/api/books');
const json = await res.json();
return { books: json }
}
render() {
var books = this.props.books;
For some reason "books" in the render function is undefined. Why doesn't getInitialProps work in a component?
getInitialProps can only be added to the default component exported by a page, adding it to any other component won't work.
getInitialProps works only at pages level, not at components level.
sgetInitialProps can not be used in children components, only in the default export of every page
https://nextjs.org/docs/api-reference/data-fetching/getInitialProps#caveats

Error, when dynamically import a react js module with webpack

I want to dynamically import a react js module. The module is not component it is an object of data, so I cannot use react code splitting. In the webpack docs there is an example with a promise. When I use it like that in a react component, it throws an error because the component tries to render before the promise hadd been resolved. I want to import it in that way in case the data does not exist, I could provide default data.
const dataProps = import(`./dataObject.js`).then(data=> data);
...
render() {
<SomeComponente data={data} />
}
I think the best way to do this is something like:
const dataProps = import(`./dataObject.js`); // Start the importing
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state {
data: null
}
}
componentDidMount() {
dataProps.then(data => this.setState({ data });
}
render() {
if (this.state.data !== null) {
return <SomeComponent data={this.state.data} />
}
return null;
}
}
This way when the import is done only then will you actually render anything

Meteor loading data with React Komposer

I'm trying to load data using React Komposer and I'm not sure what I'm doing wrong, pretty sure this is the way it should be unless I miss something. But I'm not getting any data in the UI. Could use the help
container.js
import { composeWithTracker } from 'react-komposer';
import RightNavBar from './right-nav-bar.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
const currentUser = 'bbbb';
onData(null, currentUser);
}
export default composeWithTracker(composer)(RightNavBar);
My component
export class RightNavBar extends React.Component {
render() {
return (
<div>
aaaa {currentUser}
</div>
);
}
}
Here is the "standard" example from react-komposer's repository (adapted to your specific case)
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
if (subscription.ready()) {
const currentUser = Meteor.user(); //or whatever
onData(null, {currentUser});
};
};
Here you subscribe and when the subscription is ready, your component is rendered. Otherwise, a loading component is rendered.
The 2nd parameter to onData should be an object. It is merged with other props passed to your component and is accessible from within your component via this.props.
From within your component,the props object is available via this.props, so you can either deconstruct it or access its properties directly.
class RightNavBar extends React.Component {
render() {
const {currentUser} = this.props;
return (
<div>
Hello, {currentUser.name}!
</div>
);
}
}
Your code sends a string rather than an object and React has no way of making sense of the token currentUser from within your component.

Resources