Reputation: 802
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
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
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
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
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