Navid Naseri
Navid Naseri

Reputation: 134

How can I pass arguments to event listeners in react?

I am trying to make my code clean and readable. So I decided to create a custom hook to store my useStates there. Then I created a new file to store my event listener. Now, I have three files: Page.js, useStates.js, and Listeners.js.

The problem is that I cannot use my states in event listeners.


I tried to store my states in global scope variables in useStates.js and pass them with getters. But it didn't work because updating the state didn't change the page(But it did rerender).


useStates.js:

import react, { useState } from 'react';

export default () => {
  const [myState, setMyState] = useState(false);
  return { myState, setMyState };
}

Page.js:

import react from 'react';
import useStates from './useStates';
import { someActionListener } from './listeners';

export default () => {
  const states = useStates();

  return <SomeComponent
            somProp={states.myState}
            onSomeAction={ someActionListener } />
}

Listeners.js:

export const someActionListener = (e) => {
  // This should be done
  states.setMyState(!states.myState);
} 

Upvotes: 2

Views: 1698

Answers (1)

Drew Reese
Drew Reese

Reputation: 202761

You could pass the states into the listener:

export const someActionListener = (e, states) => {
  // This should be done
  states.setMyState(!states.myState);
}

...

<SomeComponent
  somProp={states.myState}
  onSomeAction={e => someActionListener(e, states)}
/>

Or curry the states value instead, leading to a more clean UI, it saves the anonymous function callback:

export const someActionListener = states =>  e => {
  // This should be done
  states.setMyState(!states.myState);
}

...

<SomeComponent
  somProp={states.myState}
  onSomeAction={someActionListener(states)}
/>

I suggest tweaking the action listener to take a callback instead. Event listeners typically do this anyway. A curried function is an easy way to close over a callback and return the event listener callback. This decouples the state and state updating from the event listener logic, allowing the React component using both to maintain control of the state and how it's updated.

export const someActionListener = (callback) => (e) => {
  // This should be done
  callback();
}

Pass in the state updater function as a callback.

export default () => {
  const states = useStates();

  return (
    <SomeComponent
      somProp={states.myState}
      onSomeAction={someActionListener(
        () => states.setMyState(!states.myState))
      )}
    />
  );
}

Or using a functional state update since you are toggling a state value. This is to avoid stale state enclosures in callbacks.

<SomeComponent
  somProp={states.myState}
  onSomeAction={someActionListener(
    () => states.setMyState(states => ({
       ...states
       myState: !states.myState
    })
  }
/>

Upvotes: 2

Related Questions