201

Instead of writing my components inside a class, I'd like to use the function syntax.

How do I override componentDidMount, componentWillMount inside function components?
Is it even possible?

const grid = (props) => {
    console.log(props);
    let {skuRules} = props;

    const componentDidMount = () => {
        if(!props.fetched) {
            props.fetchRules();
        }
        console.log('mount it!');
    };
    return(
        <Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
            <Box title="Sku Promotion">
                <ActionButtons buttons={actionButtons} />
                <SkuRuleGrid 
                    data={skuRules.payload}
                    fetch={props.fetchSkuRules}
                />
            </Box>      
        </Content>  
    )
}
thomaux
  • 19,133
  • 10
  • 76
  • 103
Aftab Naveed
  • 3,652
  • 3
  • 26
  • 40
  • 3
    functional components are not supposed to have lifecycle methods. because they are just functions. and functions don't have methods. there are classes for that – avalanche1 Oct 24 '18 at 11:10

11 Answers11

239

Edit: With the introduction of Hooks it is possible to implement a lifecycle kind of behavior as well as the state in the functional Components. Currently

Hooks are a new feature proposal that lets you use state and other React features without writing a class. They are released in React as a part of v16.8.0

useEffect hook can be used to replicate lifecycle behavior, and useState can be used to store state in a function component.

Basic syntax:

useEffect(callbackFunction, [dependentProps]) => cleanupFunction

You can implement your use case in hooks like

const grid = (props) => {
    console.log(props);
    let {skuRules} = props;

    useEffect(() => {
        if(!props.fetched) {
            props.fetchRules();
        }
        console.log('mount it!');
    }, []); // passing an empty array as second argument triggers the callback in useEffect only after the initial render thus replicating `componentDidMount` lifecycle behaviour

    return(
        <Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
            <Box title="Sku Promotion">
                <ActionButtons buttons={actionButtons} />
                <SkuRuleGrid 
                    data={skuRules.payload}
                    fetch={props.fetchSkuRules}
                />
            </Box>      
        </Content>  
    )
}

useEffect can also return a function that will be run when the component is unmounted. This can be used to unsubscribe to listeners, replicating the behavior of componentWillUnmount:

Eg: componentWillUnmount

useEffect(() => {
    window.addEventListener('unhandledRejection', handler);
    return () => {
       window.removeEventListener('unhandledRejection', handler);
    }
}, [])

To make useEffect conditional on specific events, you may provide it with an array of values to check for changes:

Eg: componentDidUpdate

componentDidUpdate(prevProps, prevState) {
     const { counter } = this.props;
     if (this.props.counter !== prevState.counter) {
      // some action here
     }
}

Hooks Equivalent

useEffect(() => {
     // action here
}, [props.counter]); // checks for changes in the values in this array

If you include this array, make sure to include all values from the component scope that change over time (props, state), or you may end up referencing values from previous renders.

There are some subtleties to using useEffect; check out the API Here.


Before v16.7.0

The property of function components is that they don't have access to Reacts lifecycle functions or the this keyword. You need to extend the React.Component class if you want to use the lifecycle function.

class Grid extends React.Component  {
    constructor(props) {
       super(props)
    }

    componentDidMount () {
        if(!this.props.fetched) {
            this.props.fetchRules();
        }
        console.log('mount it!');
    }
    render() {
    return(
        <Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
            <Box title="Sku Promotion">
                <ActionButtons buttons={actionButtons} />
                <SkuRuleGrid 
                    data={skuRules.payload}
                    fetch={props.fetchSkuRules}
                />
            </Box>      
        </Content>  
    )
  }
}

Function components are useful when you only want to render your Component without the need of extra logic.

Milad ranjbar
  • 569
  • 1
  • 8
  • 17
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 1
    As I said, you have a logic in your component and you requirement wants you to use a lifecycle function and you you cant do that with functioanl components. So better make use of class. Use functional component when your component contains no extra logic – Shubham Khatri Jun 12 '17 at 18:41
  • 2
    It should be notice that this is not exact componentDidUpdate equivalent. `useEffect(() => { // action here }, [props.counter])` is triggered on initial render while componentDidUpdate doesn't. – Estus Flask Jan 24 '19 at 10:53
  • 4
    `passing an empty array as second argument triggers the callback in useEffect only after the initial render` this sounds like a dirty hacky way to build stuff :/ hopefully react team will come up with something better in future releases. – Lukas Liesis Aug 16 '19 at 20:11
  • 8
    so? where is the part where you answer how to run code on componentwillmount? – Toskan Feb 21 '20 at 08:18
65

You can use react-pure-lifecycle to add lifecycle functions to functional components.

Example:

import React, { Component } from 'react';
import lifecycle from 'react-pure-lifecycle';

const methods = {
  componentDidMount(props) {
    console.log('I mounted! Here are my props: ', props);
  }
};

const Channels = props => (
<h1>Hello</h1>
)

export default lifecycle(methods)(Channels);
Arturo Volpe
  • 3,442
  • 3
  • 25
  • 40
Yohann
  • 800
  • 10
  • 15
  • 3
    What is `Grid`? I don't see it defined anywhere in your code snippet? If you wanted to use redux with this as well could you get away with something like `export default lifecycle(methods)(connect({},{})(ComponentName))` ? – Sean Clancy May 04 '18 at 15:16
  • @SeanClancy Sorry for late response. The code snippet was updated. – Yohann Jul 10 '18 at 20:15
  • 1
    Is this concidered a good practice? Should i try different solutions before i reach for this one or is it okay to use it if i find it easiest? – SuperSimplePimpleDimple Apr 06 '19 at 12:55
33

You can make your own "lifecycle methods" using hooks for maximum nostalgia.

Utility functions:

import { useEffect, useRef } from "react";

export const useComponentDidMount = handler => {
  return useEffect(() => handler(), []);
};

export const useComponentDidUpdate = (handler, deps) => {
  const isInitialMount = useRef(true);

  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;

      return;
    }

    return handler();
  }, deps);
};

export const useComponentWillUnmount = handler => {
  return useEffect(() => handler, []);
};

Usage:

import {
  useComponentDidMount,
  useComponentDidUpdate,
  useComponentWillUnmount
} from "./utils";

export const MyComponent = ({ myProp }) => {
  useComponentDidMount(() => {
    console.log("Component did mount!");
  });

  useComponentDidUpdate(() => {
    console.log("Component did update!");
  });

  useComponentDidUpdate(() => {
    console.log("myProp did update!");
  }, [myProp]);

  useComponentWillUnmount(() => {
    console.log("Component will unmount!");
  });

  return <div>Hello world</div>;
};  
Etienne Martin
  • 10,018
  • 3
  • 35
  • 47
9

Solution One: You can use new react HOOKS API. Currently in React v16.8.0

Hooks let you use more of React’s features without classes. Hooks provide a more direct API to the React concepts you already know: props, state, context, refs, and lifecycle. Hooks solves all the problems addressed with Recompose.

A Note from the Author of recompose (acdlite, Oct 25 2018):

Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for Hooks. Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. Your existing code with Recompose will still work, just don't expect any new features.

Solution Two:

If you are using react version that does not support hooks, no worries, use recompose(A React utility belt for function components and higher-order components.) instead. You can use recompose for attaching lifecycle hooks, state, handlers etc to a function component.

Here’s a render-less component that attaches lifecycle methods via the lifecycle HOC (from recompose).

// taken from https://gist.github.com/tsnieman/056af4bb9e87748c514d#file-auth-js-L33

function RenderlessComponent() {
  return null; 
}

export default lifecycle({

  componentDidMount() {
    const { checkIfAuthed } = this.props;
    // Do they have an active session? ("Remember me")
    checkIfAuthed();
  },

  componentWillReceiveProps(nextProps) {
    const {
      loadUser,
    } = this.props;

    // Various 'indicators'..
    const becameAuthed = (!(this.props.auth) && nextProps.auth);
    const isCurrentUser = (this.props.currentUser !== null);

    if (becameAuthed) {
      loadUser(nextProps.auth.uid);
    }

    const shouldSetCurrentUser = (!isCurrentUser && nextProps.auth);
    if (shouldSetCurrentUser) {
      const currentUser = nextProps.users[nextProps.auth.uid];
      if (currentUser) {
        this.props.setCurrentUser({
          'id': nextProps.auth.uid,
          ...currentUser,
        });
      }
    }
  }
})(RenderlessComponent);
dance2die
  • 35,807
  • 39
  • 131
  • 194
Shivam
  • 3,091
  • 4
  • 30
  • 45
8

componentDidMount

useEffect(()=>{
   // code here
})

componentWillMount

useEffect(()=>{

   return ()=>{ 
                //code here
              }
})

componentDidUpdate

useEffect(()=>{

    //code here
    // when userName state change it will call     
},[userName])
  • 2
    @Somitya - Is it possible that you should have written "componentWillUnmount" instead of "componentWillMount"? – A-S Nov 28 '21 at 07:49
2

According to the documentation:

import React, { useState, useEffect } from 'react'
// Similar to componentDidMount and componentDidUpdate:

useEffect(() => {


});

see React documentation

DevB2F
  • 4,674
  • 4
  • 36
  • 60
2

Short and sweet answer

componentDidMount

useEffect(()=>{
   // code here
})

componentWillUnmount

useEffect(()=>{

   return ()=>{ 
                //code here
              }
})

componentDidUpdate

useEffect(()=>{

    //code here
    // when userName state change it will call     
},[userName])
  • 1
    this is exactly the same answer by Soumitya Chauhan (above answered on Nov 6th, 2021) it's identical like a copy paste – user1318393 Nov 19 '22 at 17:28
1
import React, { useState, useEffect } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);

  // componentDidMount
  useEffect(() => {
    console.log("The use effect ran");
  }, []);

  // // componentDidUpdate
  useEffect(() => {
    console.log("The use effect ran");
  }, [count, count2]);

  // componentWillUnmount
  useEffect(() => {
    console.log("The use effect ran");
    return () => {
      console.log("the return is being ran");
    };
  }, []);

  useEffect(() => {
    console.log(`The count has updated to ${count}`);
    return () => {
      console.log(`we are in the cleanup - the count is ${count}`);
    };
  }, [count]);

  return (
    <div>
      <h6> Counter </h6>
      <p> current count: {count} </p>
      <button onClick={() => setCount(count + 1)}>increment the count</button>
      <button onClick={() => setCount2(count2 + 1)}>increment count 2</button>
    </div>
  );
};

export default Counter;
0

You can make use of create-react-class module. Official documentation

Of course you must first install it

npm install create-react-class

Here is a working example

import React from "react";
import ReactDOM from "react-dom"
let createReactClass = require('create-react-class')


let Clock = createReactClass({
    getInitialState:function(){
        return {date:new Date()}
    },

    render:function(){
        return (
            <h1>{this.state.date.toLocaleTimeString()}</h1>
        )
    },

    componentDidMount:function(){
        this.timerId = setInterval(()=>this.setState({date:new Date()}),1000)
    },

    componentWillUnmount:function(){
        clearInterval(this.timerId)
    }

})

ReactDOM.render(
    <Clock/>,
    document.getElementById('root')
)
Chandan Purohit
  • 2,283
  • 1
  • 17
  • 16
0

if you using react 16.8 you can use react Hooks... React Hooks are functions that let you “hook into” React state and lifecycle features from function components... docs

WAEX
  • 115
  • 1
  • 9
0

Yeah, it's possible to make your own life cycle method by using the useEffect and useRef hook functions. I highly recommend making three utils functions and using them frequently inside your function component body or inside any other hook function.

// useDidMount hook

import { useEffect } from 'react';
import type { EffectCallback } from '@types';

const useDidMount = (callback: EffectCallback) => {
  useEffect(callback, []);
};

export default useDidMount;
// useDidUpdate hook

import { useEffect, useRef } from 'react';
import type { EffectCallback, DependencyList } from 'react';

const useDidUpdate = (callback: EffectCallback, deps: DependencyList): void => {
  const mounted = useRef<boolean>();

  useEffect(() => {
    if (!mounted.current) {
      mounted.current = true;
    } else {
      callback();
    }
  }, deps);
};

export default useDidUpdate;
// useWillUnmount hook

import useDidMount from './useDidMount';
import type { EffectCallback } from '@types';

const useWillUnmount = (callback: EffectCallback) => {
  useDidMount(() => () => {
    callback();
  });
};

export default useWillUnmount;

Easily inside your function component's body or other hook functions, you can use them and pass a callback function.

AmerllicA
  • 29,059
  • 15
  • 130
  • 154