I am currently generating the useEffect has a missing dependency
warning and I'm having a tough time understanding how to change the logic so that it is idiomatic and "correct".
To start, I have an HOC that displays an alert dialog. This HOC provides a prop to the wrapped component to display this alert dialog with a message. Here's the HOC:
function withAlertDialog(WrappedComponent) {
return function(props) {
const [dialogOps, setDialogOps] = useState({open: false, message: "", // additional properties});
const closeDialog = () => {
setDialogOps({...dialogOps, open: false});
}
const openDialog = message => {
setDialogOps({...dialogOps, open: true, message});
}
return (
<React.Fragment>
<WrappedComponent
{...props}
onOpen={openDialog}
onClose={closeDialog} />
<MyDialogComponent
open={dialogOps.open}
message={dialogOps.message} />
</React.Fragment>
)
}
}
The reason I use this HOC is so that wrapped components have an easy way of displaying a dialog without having to worry about managing its state. This really helps when there are several components that need to display a basic alert dialog, a lot of code is slimmed down.
Now I'm trying to use this in a component. The idea is that once a user has been retrieved, I'd like to make an api call to retrieve some data for this user. The effect should only run when a the user has changed, and the api call should only run once. Additionally, this user is coming from redux. Here's the component:
function MyComponent(props) {
const user = useSelector(state => state.user); // New use selector hook from react-redux
const {onOpen} = props;
useEffect(() => {
async function retrieveDataAsync() {
let client = new MyClient(); // Some generic client
try {
let response = await client.getAsync(); // Some generate data retrieval function
console.log(response.data);
} catch (error) {
onOpen(error.errorMessage);
}
}
if (user) retrieveDataAsync();
}, [user]);
}
export default withAlertDialog(MyComponent);
Now I get the error message in the console that the dependency onOpen
is missing. How exactly do I restructure this to be "correct" in the new react hooks way?
I don't want to add onOpen
as a dependency because MyComponent
gets re-rendered several times by a parent component for other reasons, this means that onOpen
will always be different from previous, which means my api call that should only happen once, is now executed every time MyComponent
is re-rendered.
I tried to use useCallback
on the onOpen
function:
const openDialog = useCallback(message => {
setDialogOps({...dialogOps, open: true, message});
}, [dialogOps]);
The problem now is that whenever dialogOps
changes, this triggers the useEffect
to run again, because the function has changed.
I am now stuck and not sure how to resolve this.