Reputation: 134
I am trying to make my code clean and readable. So I decided to create a custom hook to store my useState
s 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
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