Ghost
Ghost

Reputation: 407

Javascript click event listener with react function

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

Answers (2)

rakesh shrestha
rakesh shrestha

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

davood Sandoghsaz
davood Sandoghsaz

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

Related Questions