Trying to add some code within useEffect in my react component - reactjs

I am learning react and trying to use useEffect. But I am getting this 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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
To address #1 I checked the react and react-dom versions, they're the same 18.2.0.
To address #2 I don't think I am (code coming soon). I'm not sure how to check for #3, I don't think there is but then again not sure. Here is my code:
import React, { useEffect } from "react";
useEffect(() => {
document.title = `Greetings to aaaa`;
}, []);
class CustomerOnboarding extends React.Component {
render() {
return (
<div></div>
);
}
}
export default CustomerOnboarding;
Above code throws Invalid hook call error. However when I put the useEffect within the component class I get a slightly different error:
I don't know if this is related, but I am using react with ruby on rails. I have other components working just fine, without hooks that is. Any ideas?

useEffect used for functional componet. for class component you need to use
componentDidUpdate and componentDidMount
In your case :
import React from "react";
class CustomerOnboarding extends React.Component {
componentDidMount() { // this is for class component
document.title = `Greetings to aaaa`;
}
render() {
return (
<div></div>
);
}
}
export default CustomerOnboarding;
for using of useEffect you can change your Component to functional component :
import React, { useEffect } from "react";
function CustomerOnboarding() {
useEffect(()=>{
document.title = `Greetings to aaaa`;
},[]);
return (
<div></div>
);
}
export default CustomerOnboarding;

or you can convert your component to function like below
import React, { useEffect } from "react";
const CustomerOnboarding = () => {
useEffect(() => {
document.title = `Greetings to aaaa`;
}, []);
return <div></div>;
};
export default CustomerOnboarding;

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

Jest mocking a functional component

I feel like I'm missing something. I'm trying to mock a functional component and it keeps giving me an error
If I do
TestRenderer.create(
<FunctionalComponent/>
)
It gives the error
Invariant Violation: FunctionalComponent(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
If I change it to a class based component it works.
How can I mock a functional component? thanks!
edit:
I tried mocking this component
const SampleComponent = () => {
return <View/>
}
And it gives the the error,
But this one
class SampleClassComponent extends Component {
render() {
return <View/>
}
}
Does not
error happens when running
jests.mock('path/to/component')
You have to return something in your functional component, even if it is null. The functional component is Link.
import React from 'react';
import TestRenderer from 'react-test-renderer';
function Link(props) {
return null;
}
const testRenderer = TestRenderer.create(
<Link/>
);
console.log(testRenderer.toJSON());
export default Link;
or
import React from 'react';
import TestRenderer from 'react-test-renderer';
function Link(props) {
return <a href={props.page}>{props.children}</a>;
}
const testRenderer = TestRenderer.create(
<Link/>
);
console.log(testRenderer.toJSON());
export default Link;
References:
Facebook, Inc. "Test Renderer". React. https://reactjs.org/docs/test-renderer.html (accessed October 8, 2019).

How to use react-alert with React component that is defined as ES6 class

I am trying to use https://www.npmjs.com/package/react-alert in my project, but the examples are porvided for the React components that are declared as functions but my React components are declared as classes like:
class ConnectedOrderForm extends Component {
//const statusAlert = useAlert();
constructor(props) {
super(props);
}
render() {
return (
<div>Test</div>
)
}
}
const OrderForm = withRouter(connect(mapStateToProps, mapDispatchToProps)(ConnectedOrderForm));
export default OrderForm;
When I am trying to call useAlert().show('OK'); from class event, I am getting:
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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
So, how can I use this react-alert hook from ES6 class component?
The solution was to use either export code:
const OrderForm = withRouter(connect(mapStateToProps, mapDispatchToProps)(ConnectedOrderForm));
export default withAlert()(OrderForm);
const OrderForm = withAlert()(withRouter(connect(mapStateToProps, mapDispatchToProps)(ConnectedOrderForm)));
export default OrderForm;
And call:
this.props.alert.show()
Based on the docs it would seem that you should not be using the useAlert hook but rather the withAlert HOC. Hooks can only be used in functional components, and since you want to use a class, you would need to the withAlert HOC.
Here is an example of that that would look like
import { withAlert } from 'react-alert'
class App extends React.Component {
render() {
const alert = this.props.alert;
return (
<button
onClick={() => {
alert.show('Oh look, an alert!')
}}
>
Show Alert
</button>
);
}
}
export default withAlert()(App)
Did you try this one?
import {alert} from 'react-alert';
alert.show('Some message', {
... options
})
UPD. Sorry, this is wrong answer, Please take a look here:
https://github.com/schiehll/react-alert/issues/116

React - How to change code written in the old way when using a new approach?

I was looking for steps on the internet to refactor previous applications for a new approach, but I did not find a satisfactory answer...
I know the previous approach for creating applications in ReactJS which uses the Component and render() function for example, but I can see that it is currently different because:
npx create-react-app app-name
is now generating a different template.
For example, previously it was imported:
import React, {Component} from 'react';
and now only:
import React from 'react';
I am asking for tips or simple advice on what I should change so that the old code works without importing the Component.
Has the method of using functions for communication via AJAX (e.g. loadPerson) changed as well?
For example here is some not working example of ./src/PersonDetail.js:
import React from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
export class PersonDetail {
constructor(props) {
super(props);
this.state = {person: null};
}
async componentDidMount() {
const {personId} = this.props.match.params;
const person = await loadPerson(personId);
this.setState({person});
}
render() {
const {person} = this.state;
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
);
}
}
Thank you in advance.
With the introduction of hooks some core concepts begin to change. Before React 16.8 we used to have a rule of thumb to decide rather a component should be based on class or function:
If the component should hold state then it should be class based. If it doesn't have state (stateless) then it could be a functional component
This used to be true cause there wasn't a way to implement stateful logic inside functional components. Now hooks allow you to implement state in functional components.
The boilerplate generated by create-react-app doesn't import Component from react anymore cause only class based components need to extends from Component and App is now a functional component.
Nothing changed really it's just another way to write your components.
Just import like it used to be:
export class PersonDetail extends React.Component
or give hooks a chance and turn your component into a functional component:
import React, { useState, useEffect } from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
const PersonDetail = ({ personID }) => {
const [person, setPerson] = useState(false)
useEffect(() => {
const person = await loadPerson(personId)
setPerson(person)
}, [personID])
return !person ? null : (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
)
}
To add to other comments...
You can have class-based components side by side to functional components, there is no inherent need to re-write any old class-based components you have written.
Introducing Hooks
No Breaking Changes
Before we continue, note that Hooks are:
Completely opt-in. You can try Hooks in a few components without rewriting any existing code. But you don’t have to learn or use Hooks right now if you don’t want to.
100% backwards-compatible. Hooks don’t contain any breaking changes.
Available now. Hooks are now available with the release of v16.8.0.
I am assuming the "new approach" you are talking about are React Hooks.
import * as React from "react";
import { DetailList } from "./DetailList";
import { loadPerson } from "./requests";
const PersonDetail = props => {
const [person, setPerson] = React.useState(null);
React.useEffect(() => {
(async () => {
const { personId } = props.match.params;
setPerson(await loadPerson(personId));
})();
}, []);
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
);
};
export { PersonDetail };
The change you noticed is that create-react-app now creates functional components. This means your components are no longer classes but just functions.
You can still import Component and export a class that extends Component.
You don't need to write your class as a function but to write the example class as a functional component you can do the following:
import React, { useEffect, useState } from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
export default props => {
const { personId } = props.match.params;
const [person, setPerson] = useState(null);
useEffect(() => {
loadPerson(personId).then(person => setPerson(person));
}, [personId]);
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">
Details at {person.name}
</h5>
<DetailList details={person.details} />
</div>
);
};
You can read more about react hooks here
With this import,
import React from 'react';
You can extend Component without importing like,
export class PersonDetail extends React.Component{ ...}
Update
Hooks are newly added in React 16.8 and it is not recommended that we should change our exsiting code with Hooks. We can still have our exsiting class-based approach which extends Component or React.Component.
Hooks gives us the capability of maintaining state of the component as well as it gives a space to write React lifecycle methods.
For example, state in class-based component
state = {
stateVariable : "stateValue"
}
In new approach, it is equivalent to
const [stateVariable, setStateVariable] = useState("stateValue")
And for the lifecycle methods we have useEffect.
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
useEffect(() => {
console.log(stateVariable);
});
of-course we need to import useState and useEffect from react package,
import React, {useState, useEffect} from 'react'
Finally, the class-based component
import React from 'react'
class MyComponent extends React.Component{
state={
stateVariable : "stateValue"
}
componentDidMount(){
console.log(this.state.stateVariable)
}
render(){
return(
<div> {this.state.stateVariable} </div>
)
}
}
can be converted to functional component like,
import React, {useState, useEffect} from 'react'
const MyComponent = (props) => {
const [stateVariable, setStateVariable] = useState("stateValue")
useEffect(()=>{
console.log(stateVariable)
})
return(
<div> {stateVariable} </div>
)
}
Note: We don't have access to this in functional component because we don't have class anymore.
If you want to use class components, you have to make them extend React.Component.
There is another way to create components now, which is the functional way. Meaning that a component can be a simple JavaScript function returning an element (usually, some JSX). In this case, you don't need to import React.Component anymore.
Class component:
import React, { Component } from "react"
class PersonDetail extends Component {
render() {
...
}
...
}
Functional component (this is the way React and the community is now pushing for):
import React from "react"
const PersonDetail = () => {
return (<Your JSX code>)
}

reactjs: how to define componentDidMount inside ES6 const?

I have this react component that is not an ES6 class. It's a const like :
import React from 'react';
import Dashboard from './components/Dashboard';
const Home = (props) => {
const componentDidMount = () => {
console.log('component mounted'); // not working
}
return <Dashboard />;
}
Inside this const how do i define the componentDidMount function like i would do in a normal ES6 class? this is how i did it before.
import React from 'react';
import Dashboard from './components/Dashboard';
class Dashboard extends React.Component {
componentDidMount() {
console.log('component mounted');
}
render() {
return <Dashboard />;
}
}
Stateless functional components don't support the lifecycle methods.
You can either convert it to a stateful component or wrap it in a stateful container.
Good example of wrapping to get the lifecycle methods:
https://egghead.io/lessons/javascript-redux-fetching-data-on-route-change
To anyone else learning React and stumbling upon this in the future like me, the accepted answer here is out of date. Please see this question: componentDidMount equivalent on a React function/Hooks component?
If using React 16.8.0+ you can create the effect of componentDidMount by using the hook useEffect:
useEffect(() => {
// Your code here
}, []);

Resources