Let me start with the working code that I'm trying to refactor. I'm trying to abstract button creation, and whatever I try, I keep getting the "hooks can only be called inside of the body of a functional component" error.
Following along with a tutorial, I've setup a navigation bar and a button that will take me from the home screen to a "Details" screen. Here's what works:
const DetailsButton = (): JSX.Element => { const navigation = useNavigation(); return (<Button title="Details" onPress={() => navigation.navigate('Details')} /> );};// And then later on...<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Master', headerRight: DetailsButton }} />
That button will take me to the Details screen with no issue. But then I got to thinking that I could refactor this into a kind of button factory function:
interface BarButtonProps { title: string, destination: string,};const BarButton = (buttonProps: BarButtonProps): () => JSX.Element => { const navigation = useNavigation(); return (): JSX.Element => (<Button title={buttonProps.title} onPress={() => navigation.navigate(buttonProps.destination)} /> );}const DetailsButton = BarButton({title: 'Details', destination: 'Details'});
My plan was to use the DetailsButton
component as before. However, when I try to run the code, I get the hooks error. I thought I understood why, and so I tried the following variation:
const BarButton = (buttonProps: BarButtonProps): () => JSX.Element => { return (): JSX.Element => (<Button title={buttonProps.title} onPress={() => useNavigation().navigate(buttonProps.destination)} /> );}
In my mind, the hook now seemed to be within the functional component. But that only got me as far as the home screen. When I press the button I get the same hooks error again. I must be missing some subtle point.
Is it possible to create a higher-order function like this, one that uses a hook, or am I trying something impossible? If it's possible, can someone show me what would work? Thanks.