reactjs: how to define componentDidMount inside ES6 const? - reactjs

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
}, []);

Related

Trying to add some code within useEffect in my react component

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;

How to replace useEffect hook with component lifecycle?

I'm forced to use class based component, so how can I replace useEffect with component lifecycle like componentDidMount, componentDidUpdate and componentWillUnmount in my React component.
Please help
const App = () => {
useEffect(() => {
store.dispatch(loadUser());
}, []);
As React team mentioned in this doc
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
So if you want to use life cycles you have to use class
.
Use class that extends Component and it must be like this :
import React from 'react';
class App extends React.Component {
//you can use components lifecycle here for example :
componentDidMount() {
store.dispatch(loadUser());
}
}
After delcaring the constructor you should put the componentDidMount method just ahead, like this:
class yourComponent extends Component {
constructor(props){
// constructor taks...
}
componentDidMount() {
// what you want to do when the component mount
}
}

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

About lifecycle hooks in React

I have a higher order component as below. I have difficulty in understanding in which mounts first. See the below:
MAIN COMPONENT (ADD EMPLOYEE)
import React, {useEffect} from 'react';
const AddEmployee = (props) => {
useEffect(()=>{
console.log("USE EFFECT");
},[])
return (//SOME JSX)
}
export default GlobalAxiosHandler(AddEmployee);
HIGHER ORDER COMPONENT
import React, { Component } from 'react';
const GlobalAxiosHandler = (WrappedComponent) => {
return class extends Component {
componentDidMount () {
console.log('GLOBAL COMPONENTDIDMOUNT');
}
render() {
console.log("RENDER GLOBAL");
return (
<React.Fragment>
<WrappedComponent {...this.props}></WrappedComponent>
</React.Fragment>)
}
}
}
The console output is as below
RENDER GLOBAL
GLOBAL COMPONENTDIDMOUNT
USE EFFECT
My question here is: Since wrapped component act as a child component to HOC, hence the componentDidMount() of HOC should get executed after useEffect() in the wrapped component. In such a case the output should've been like the below:
RENDER GLOBAL
USE EFFECT
GLOBAL COMPONENTDIDMOUNT
But the reverse happened. Can you please explain why. Hope I've articulated it properly.
EDIT: When I use the hooks in the HOC too as below (Using HOOKS both in HOC and Wrapped component):
import React, { useEffect, Component } from 'react';
const GlobalAxiosHandler = (WrappedComponent, axios) => {
const GlobalAxiosHandlerInner = (props) => {
useEffect(()=>{
console.log("USE EFFECT HOC");
})
return (
<React.Fragment>
<WrappedComponent {...props}></WrappedComponent>
</React.Fragment>)
}
return GlobalAxiosHandlerInner;
}
export default GlobalAxiosHandler;
I am seeing output as below (As I anticipated):
USE EFFECT WRAPPER //(THIS IS FROM THE WRAPPED COMPONENT)
USE EFFECT HOC
Can you please explain why the reversal of life cycle events happening?
useEffect is equivalent to componentDidMount (also didUpdate and willUnmount) so it only gets called after the first render, that's exactly what's happening:
render from HOC
render form WrappedComponent
ComponentDidMount from HOC
useEffect from WrappedComponent
The console output should be:
RENDER GLOBAL
GLOBAL COMPONENTDIDMOUNT
USE EFFECT
Nothing wrong happening here

Extend React lifecycle hook (e.g add a print statement on every ComponentDidMount)

I want to add some behaviour on a given lifecycle hook of a React application.
For example, adding a console.log('Component is mounted') on every ComponentDidMount of all the components of an application, without having to define it in every one of them (as a decorator for example), sort of like a global extender of that method that adds some code to it. Like that: Extending Vue Lifecycle Hooks but for React.
Anyone has an idea on how to achieve that? Cheers!
You can use hoc. In the root app, apply the higher order component.
Example:
const withMountHOC = WrappedComponent => {
return class extends Component {
componentDidMount() {
console.log('mounted');
}
render() {
return <WrappedComponent {...this.props} />
}
}
}
export default withMountHOC;
In your app component:
const WrappedApp = withMountHOC(App);
ReactDOM.render(
WrappedApp,
document.getElementById('root')
);
Since the parent componentDidMount hook is called after child componentDidMount hook, the HOC componentDidMount will be applied in any nested level of the component.
You may also be interested to see this blog: Replacing Mixins in React.
create CustomComponent.js
import React, { Component } from 'react'
class CustomComponent extends Component {
constructor(props){
super();
}
componentDidMount(){
console.log('component is mounted');
}
render () {
return (
<div>
{this.props.children}
</div>
)
}
}
export default CustomComponent
Now create MyComponent.js that extends CustomComponent.js
import React, { Component } from 'react'
import CustomComponent from './CustomComponent'
class MyComponent extends CustomComponent {
render () {
return (
<div>
Hello from MyComponent
</div>
)
}
}
export default MyComponent;
now you see console , you have log : "component is mounted"
but if you write componentDidMonunt() inside MyComponent.js , you will get log from MyComponent.js

Resources