Reputation: 407
I want to create a custom hook in which I add a click event listener to a DOM element which calls a function defined in a React component which uses a state variable.
I add the event listener, but when I call the function, it does not reflect any state changes, it is always taking the initial state value.
const useCustomHook = (functionDefinedInComponent) => {
// logic to get the dom element
element.addEventListener('click', () => functionDefinedInComponent(item));
};
const CustomComponent = () => {
const [state, setState] = useState(...);
const customFunction = (item) => {
setState(...); // change the 'state' variable
// use item and state to do something
}
useCustomHook(customFunction);
return ...;
}
When I click the DOM element to which I added the click event, the customFunction
triggers with initial state value. Is there any to solve this?
Upvotes: 1
Views: 1864
Reputation: 1455
I meant something like this. you might have to wrap your callback function in React.useCallback as well.
const useCustomHook = (functionDefinedInComponent) => {
React.useEffect(() => {
// logic to get the dom element
element.addEventListener('click', () => functionDefinedInComponent());
}, [functionDefinedInComponent])
};
Can you try this out and let me know what sort of problem you get.
Here is a code sandbox that you were trying to do. https://codesandbox.io/s/rakeshshrestha-nvgl1?file=/src/App.js
Explanation for the codesandbox example
Create a custom hook
const useCustomHook = (callback) => {
React.useEffect(() => {
// logic to get the dom element
const el = document.querySelector(".clickable");
el.addEventListener("click", callback);
// we should remove the attached event listener on component unmount so that we dont have any memory leaks.
return () => {
el.removeEventListener("click", callback);
};
}, [callback]);
};
so, I created a custom hook named useCustomHook
which accepts a function as a parameter named callback
. Since, we want to attach an event on element with class clickable
, we should wait till the element gets painted on the browser. For this purpose, we used useEffect
which gets called after the component has been painted on the screen making .clickable
available to select.
const [input, setInput] = React.useState("");
const logger = React.useCallback(() => {
alert(input);
}, [input]);
useCustomHook(logger);
// render
Here, I have a state input which holds the state for the textbox. And also a function named logger
which I passed to my custom hook. Notice, that the function logger has been wrapped inside of useCallback
. You don't need to wrap it in this case, but it was there so that every time the component rerenders a new logger function won't be created except the changes in the input
state.
Upvotes: 1
Reputation: 684
You can use a public component like this:
const ClickableComponent = props => {
const { title, handleClick, component: Component } = props;
return (
<Component onClick={handleClick}>{title}</button>
)
};
export default ClickableComponent;
You can use this component like below:
<ClickableComponent title="your title" handleClick={handleClick} component={<button/> } />
Upvotes: 0