next.js - getInitialProps does not work in component - reactjs

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

Related

Calling ApolloClient GraphQl request inside componentDidMount method

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.

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

The data that comes from an API end point is undefined in the child component in React

Good day to all!
I have this situation: I use Apollo client to get data from a GraphQL API endpoint in the parent class component in React. I pass this data to the child class component. The first time everything works fine but after a page refresh the data in the child component becomes undefined and the app crashes.
Here is the representation of the situation:
The ParentComponent
import React, { Component } from 'react'
import { gql } from "apollo-boost";
import {graphql} from 'react-apollo';
import ChildComponent from './ChildComponent'
const getProducts = gql`
{
category {
products {
id
name
gallery
}
}
}
`
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
products: []
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
products: [...this.props.data.category.products]
})
}, 1000)
}
render () {
let products = this.state.products;
return (
<div><ChildComponent theProducts = {products}/></div>
)
}
}
export default graphql(getProducts)(ParentComponent);
The ChildComponent
import React, { Component } from 'react'
class ChildComponent extends Component {
constructor(props) {
super(props)
this.state = {
products: this.props.theProducts
}
}
render () {
let item = this.state.products.find(each => each.id === id);
return (
<div>
<ul>
<li>{item.name}</li>
<li><img src= {item.gallery[0]} alt="product"></img></li>
</ul>
</div>
)
}
}
export default ChildComponent;
So, when the app starts everything seems to work fine. But if I refresh the page it throws an error and says that name is undefined, gallery is undefined. It is clear that the data is not coming through to the ChildComponent. Is there a way to make sure that the data comes in at any time?
Thank you in advance.
You use theProducts in the ChildComponent but you pass theProduct from ParentComponent . And state product also has the same error. Just update to theProducts and product

How to use MSAL-React with class component?

Am using https://www.npmjs.com/package/#azure/msal-react in my react project. The library provides hooks to perform auth very easliy.
So in functional component, for getting access token,
if (account && inProgress === "none") {
instance.acquireTokenSilent({
...loginRequest,
account: account
}).then((response) => {
callMsGraph(response.accessToken).then(response => setGraphData(response));
});
}
needs to be called where const { instance, accounts, inProgress } = useMsal(); is used.
But I need to call api to fetch data from class components.
So, how to achieve the same functionality in class component
You cannot access the msal-react hooks inside your class components, so to do this you would either need to access the raw context, or wrap with a higher order component.
The following takes the examples from documentation, modified to your question about accessing instance, accounts, inProgress for subsequent API calls.
Consuming Raw Context
import React from "react";
import { MsalContext } from "#azure/msal-react";
class YourClassComponent extends React.Component {
static contextType = MsalContext;
render() {
const msalInstance = this.context.instance;
const msalAccounts = this.context.accounts;
const msalInProgress = this.context.inProgress;
// rest of your render code
}
}
}
Using Higher-Order-Component
import React from "react";
import { withMsal } from "#azure/msal-react";
class YourClassComponent extends React.Component {
render() {
const msalInstance = this.props.msalContext.instance;
const msalAccounts = this.props.msalContext.accounts;
const msalInProgress = this.props.msalContext.inProgress;
// rest of your render code
}
}
export default YourWrappedComponent = withMsal(YourClassComponent);
Either way works so personal preference really. I prefer accessing the raw context over wrapping for readability.

Components called by API won't render

I'm building an app in React. I'm new to React so please bear with me. This question is very simple. I am creating three components that are being called by an API (I'm using axios to call the API), but they simply won't render. I am not sure what I'm doing wrong.
import React, { Component } from "react";
import axios from "axios";
class Temperature extends Component {
showTemperature = () => {
let apiKey = "4cc79448ae57aa2b8557ec4dcd604fac";
let city = "Lisbon,pt";
let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;
axios.get(url).then(function(response) {
let temp = Math.round(response.data.main.temp);
return temp;
});
};
render() {
return <div>{this.showTemperature()}</div>;
}
}
export default Temperature;
and then I call the component on App.js
<div>
<Temperature />
</div>
inside the App function. Nothing is rendering when I call the component. Any tips or what am I doing wrong? Thanks.
PS: I am importing the components into App:
import Temperature from "./Temperature";
EDIT: I want to add that I have this same app done in vanilla JavaScript and the API is working fine.
Try utilizing state to store your temperature value, like so:
import React, { Component } from "react";
import axios from "axios";
class Temperature extends Component {
constructor(props) {
super(props);
this.state = {
temp: 'Error' // Add default value to help determine if error is api call.
};
this.showTemperature = this.showTemperature.bind(this); // Bind 'this' context
}
showTemperature() { // This is how to properly declare a method in react.
let apiKey = "4cc79448ae57aa2b8557ec4dcd604fac";
let city = "Lisbon,pt";
let url = `https://api.openweathermap.org/data/2.5/weather?
q=${city}&appid=${apiKey}&units=metric`;
axios.get(url).then(function(response) {
let temp = Math.round(response.data.main.temp);
if (temp) { // Don't setstate if temp is undefined.
this.setState({temp: temp}); // Set your state
}
});
};
componentDidMount() {
this.showTemperature(); // Call your method on mount.
}
render() {
return <div>{this.state.temp}</div>;
}
}
export default Temperature;
I added comments to explain further errors in your code. You should be able to troubleshoot much better from here.
You have to import Temperature in App.js like this
import Temperature from 'path'
Your showTemperature method returns undefined. That's why It's not rendering anything.

Resources