sarangkkl
sarangkkl

Reputation: 802

how to convert a hook into class based component

I have a functional component which i used to detect outside click of the component and once that done i do some logic... now i had to use this inside a class based component

My Hook useComponentVisible

import { useState, useEffect, useRef } from 'react';

export default function useComponentVisible(visible) {
  const [isComponentVisible, setIsComponentVisible] = useState(visible);
  const ref = useRef(null);

  const handleHideDropdown = (event) => {
    if (event.key === 'Escape') {
      setIsComponentVisible(false);
    }
  };

  const handleClickOutside = (event) => {
    if (ref.current && !ref.current.contains(event.target)) {
      setIsComponentVisible(false);
    }
  };

  useEffect(() => {
    document.addEventListener('keydown', handleHideDropdown, true);
    document.addEventListener('click', handleClickOutside, true);
    return () => {
      document.removeEventListener('keydown', handleHideDropdown, true);
      document.removeEventListener('click', handleClickOutside, true);
    };
  });

  return { ref, isComponentVisible, setIsComponentVisible };
}

// How i was using it in my function based component

import useComponentVisible from '../../hooks/useComponentVisible';
import { MenuItem, MenuWrapper } from './MenuDropDownStyle';

const MenuDropDown = () => {
  const { ref, isComponentVisible } = useComponentVisible(true);

  return (
    <>
      {isComponentVisible && (
        <MenuWrapper ref={ref}>
          <MenuItem to="/lectures">Your profile</MenuItem>
          <MenuItem to="/lectures">Log out</MenuItem>
        </MenuWrapper>
      )}
    </>
  );
};

export default MenuDropDown;

//i need to use the component in CurrencyDropdown

import React, { Component } from "react";
import { SelectWrapper } from "./CurrencyDropdownStyle";
import { getCurrency } from "../../../utls/MakeQuery";
import { SelectInput } from '../../';
import useComponentVisible from '../../hooks/useComponentVisible'

export class CurrencyDropdown extends Component {
  constructor() {
    super();
    this.state = {
      currency: [],
    };
    // const { ref, isComponentVisible } = useComponentVisible(true);
  }

  componentDidMount() {
    getCurrency()
      .then((res) => {
        this.setState({
          currency: res.data.currencies,
        });
        this.props.toggleCurrency("USD+$");
      })
      .catch((err) => {
        console.log(err);
      });
  }

  // Function That will handle the change of the currency in the state(●'◡'●)
  handleToggleCurrency(value){
    this.props.toggleCurrency(value)
  }
  render() {
    
    return <SelectWrapper>
        {this.state.currency ? this.state.currency.map((item,index)=>(
                <SelectInput key={index} value={`${item.label}+${item.symbol}`} label={`${item.symbol} ${item.label}`}/>
            )):""}
    </SelectWrapper>;
  }
}

export default CurrencyDropdown;

Upvotes: 0

Views: 996

Answers (4)

sarangkkl
sarangkkl

Reputation: 802

//Here is the way i solved it

import React, { Component } from "react";
import { SelectWrapper } from "./CurrencyDropdownStyle";
import { getCurrency } from "../../../utls/MakeQuery";
import { SelectInput } from '../../';

export class CurrencyDropdown extends Component {
  constructor() {
    super();
    this.state = {
      currency: [],
      isComponentVisible: true,
      ref: React.createRef(null),
    };
  }

  handleHideDropdown = (event) => {
    if (event.key === 'Escape') {
      this.setState({ isComponentVisible: false });
    }
  }

  handleClickOutside = (event) => {
    if (this.state.ref.current && !this.state.ref.current.contains(event.target)) {
      this.setState({ isComponentVisible: false });
    }
  }

  componentDidMount() {
    getCurrency()
      .then((res) => {
        this.setState({
          currency: res.data.currencies,
        });
        this.props.toggleCurrency("USD+$");
      })
      .catch((err) => {
        console.log(err);
      });

      document.addEventListener('keydown', this.handleHideDropdown, true);
      document.addEventListener('click', this.handleClickOutside, true);
  }
  componentWillUnmount(){
    document.removeEventListener('keydown', this.handleHideDropdown, true);
    document.removeEventListener('click', this.handleClickOutside, true);
  }
  // Function That will handle the change of the currency in the state(●'◡'●)
  handleToggleCurrency(value){
    this.props.toggleCurrency(value)
  }
  render() {
    
    return <>
        {this.state.isComponentVisible && <SelectWrapper ref={this.state.ref}>
        {this.state.currency ? this.state.currency.map((item,index)=>(
                <SelectInput key={index} value={`${item.label}+${item.symbol}`} label={`${item.symbol} ${item.label}`}/>
            )):""}
    </SelectWrapper>}
    </>;
  }
}

export default  CurrencyDropdown;

Upvotes: 1

Nick Vu
Nick Vu

Reputation: 15520

A class-based component cannot use hooks, which are only designed for function-based components.

I'd suggest that you should convert your component to a function-based component or use a wrapper to handle hooks.

Here is how we're using a wrapper to handle your case

class CurrencyDropdownComponent extends Component {
  constructor() {
    super();
    this.state = {
      currency: [],
    };
    // const { ref, isComponentVisible } = useComponentVisible(true);
  }

  componentDidMount() {
    getCurrency()
      .then((res) => {
        this.setState({
          currency: res.data.currencies,
        });
        this.props.toggleCurrency("USD+$");
      })
      .catch((err) => {
        console.log(err);
      });
  }

  // Function That will handle the change of the currency in the state(●'◡'●)
  handleToggleCurrency(value){
    this.props.toggleCurrency(value)
  }
  render() {
    
    return <SelectWrapper>
        {this.state.currency ? this.state.currency.map((item,index)=>(
                <SelectInput key={index} value={`${item.label}+${item.symbol}`} label={`${item.symbol} ${item.label}`}/>
            )):""}
    </SelectWrapper>;
  }
}

//the function-based component as a wrapper for `CurrencyDropdown` component
function CurrencyDropdown = (props) => {
   const { ref, isComponentVisible } = useComponentVisible(true);
   return <CurrencyDropdownComponent {...props} isComponentVisible={isComponentVisible} ref={ref}>
}

export default CurrencyDropdown;

Another solution is converting your class-based component to a function-based component

function CurrencyDropdown(props) {
  const { ref, isComponentVisible } = useComponentVisible(true);
  const [currency, setCurrency] = React.useState([]) //`this.state` replacement

  //componentDidMount replacement
  useEffect(() => {
     getCurrency()
      .then((res) => {
        setCurrency(res.data.currencies);
        props.toggleCurrency("USD+$");
      })
      .catch((err) => {
        console.log(err);
      });
  }, [])

  // Function That will handle the change of the currency in the state(●'◡'●)
  handleToggleCurrency(value){
    props.toggleCurrency(value)
  }
  
  return <SelectWrapper>
        {currency ? currency.map((item,index)=>(
                <SelectInput key={index} value={`${item.label}+${item.symbol}`} label={`${item.symbol} ${item.label}`}/>
            )):""}
    </SelectWrapper>
}

export default CurrencyDropdown;

Upvotes: 0

Han Moe Htet
Han Moe Htet

Reputation: 744

You need to create a wrapper functional component for the class component.

const CurrencyDropdownWrapper = (props) => {
   const hookData = useComponentVisible(true);
   return <CurrencyDropdown {...props} hookData={hookData} />
}

Then use your hook properties from hookData prop inside your class component.

Upvotes: 1

Milos Pavlovic
Milos Pavlovic

Reputation: 1450

You can not mix functional and class based components. There is no way you can use hook inside class based component, it is completely different mental and technical model of programming react elements. You would need either to convert class based to functional(which I suggest), or to create another-like useComponentVisible mechanism but in a class based style. I would suggest to stick with either class based or functional based style, it can make you troubles when you mix both of the, especially when there is a request to share some functionalities between them.

Upvotes: 0

Related Questions