Type checking with TypeScript
There are 6 main interfaces that you'll use throughout your experience with Modalfy:
ModalComponentProp
/ModalProps
- Interface of themodal
prop exposed by the library (specifically for modal components).
ModalComponentWithOptions
- Interface that adds type support of themodalOptions
property (specifically for Hooks modal components).
The 6th and last main interface will actually be provided by you, as Modalfy v2 brought support for modal params type. That interface is going to be used mainly by
ModalProp
, ModalComponentProp
and modalfy()
. But for now: let's see how to use the other interfaces we just mentioned.Please refer to the Types section of the API reference to get a complete overview of each of these interfaces.
These interfaces should be the ones you use the less. They're the ones that will ensure the type safety of the 2 arguments
createModalStack()
. So if we were to reuse the same initial example we say in the Creating a stack section, we'd now have:TypeScript
./App.tsx
import React from 'react'
import {
ModalOptions,
ModalStackConfig,
createModalStack,
ModalProvider,
} from 'react-native-modalfy'
import Navigation from './navigation.ts'
import { ErrorModal } from './components/Modals.ts'
const modalConfig: ModalStackConfig = { ErrorModal }
const defaultOptions: ModalOptions = { backdropOpacity: 0.6 }
const stack = createModalStack(modalConfig, defaultOptions)
const App = () => (
<ModalProvider stack={stack}>
<Navigation />
</ModalProvider>
)
export default App
And from there, the type checker will get to work and let you know if you're doing something wrong.
If you directly provide the 2 objects instead of using variables like so:
createModalStack({ ErrorModal }, { backdropOpacity: 0.6 })
No need to use these 2 interfaces as
createModalStack()
is already doing it under the hood.This interface allows you to type check the
modal
prop that your regular component will get access to by using withModal()
HOC. This means that you'll have to keep a few things in mind:- If you're inside a modal component and not a "regular" component, you should use
ModalComponentProp
instead. - If you're using
useModal()
Hook, no need to employModalProp
as the Hook itself will take care of all the typing. Simply provide your params interface to the Hook as suchuseModal<ModalStackParams>()
(explained below). - The main and potentially only use case for
ModalProp
then is when you're using a Class component.
Class
Hooks
./components/PokedexCard.tsx
import React from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import { ModalProp, withModal } from 'react-native-modalfy'
import { ModalStackParams } from '../types/modals.ts'
interface OwnProps {
pokemon: ModalStackParams['PokedexEntryModal']['name']
type: ModalStackParams['PokedexEntryModal']['type']
id: ModalStackParams['PokedexEntryModal']['id']
}
type Props = ModalProp<ModalStackParams, OwnProps>
class PokedexCard extends React.Component<Props> {
onPress = () => {
const {
modal: { openModal },
pokemon,
type,
id,
} = this.props
openModal('PokedexEntryModal', { id, name: pokemon, type })
}
render() {
const { id, pokemon, type } = this.props
return (
<TouchableOpacity onPress={this.onPress}>
<View>
<Text>Nr. {id}Text>
<Text>{pokemon}</Text>
<Text>{type}</Text>
<Text>Show more</Text>
</View>
</TouchableOpacity>
)
}
}
export default withModal(PokedexCard)
// ❌ Don't use ModalProp with the useModal() Hook. If you want to fully type it
// simply provide your params interface as such: useModal<ModalStackParams>()
// (ModalStackParams is explained below).
Lots of things are happening in this snippet, but if you're already familiar with TypeScript generics, this should get you excited! Let's dissect this snippet.
#L5
with ModalStackParams
. It's an interface you'll have to build that will represent the complete tree of your modals and the types their params are expecting. From
#L7
to #L11
, we're letting TypeScript know that <PokedexCard/>
expects 3 props that should comply with the types specified in ModalStackParams
. We're doing this to ensure the type safety of these 3 props because we're using them L#24
to open 'PokedexEntryModal'
and pass them as params.If we were to write
ModalStackParams
, we can now guesstimate that it could look something like this a minima:TypeScript
interface ModalStackParams {
PokedexEntryModal: {
name: string
type: string
id: number
}
}
You can have a look at the Example provided in the repository and available on Expo to see what
ModalStackParams
could look like/be used in a real-world scenario.You'd also realize that we didn't pass
ModalStackParams
as a generic to withModal()
#L42
, instead, we directly provided it to React.Component
#L15
, via Props
created #L13
. As you may know, with TypeScript, React.Component
is a generic class that accepts up to 2 arguments: React.Component<Props, State>
. That's why ModalProp
also accepts up to 2 arguments, your params interface and your component props and returns a type with your props type + the new modal
prop. There are a few things to notice here:- If you have any
State
interface, you'll have to provide it toReact.Component
as a second argument, notModalProp
. - If your component doesn't expect any props, you don't have to provide a second argument to
ModalProp
. If you want, you can even use it without providing the params type. This means that the most basic way of usingModalProp
isclass PokedexCard extends React.Component<ModalProp>
- On the contrary, providing your params types to
ModalProp
gives you access to some sweet autocompleting experience (try to see what you get when you trigger it onopenModal()
for instance)!
These types work on the same principles as
ModalProp
with just some key differences to keep in mind. The first and most important is:ModalComponentProp
/ModalProps
should only be used with modal components (rendered by Modalfy)! If the component you're working on is not rendered by Modalfy directly/part of your
createModalStack()
config, you should use ModalProp
instead.Given that we're in a specific modal component,
ModalComponentProp
accepts a 3rd argument, corresponding to the name of the modal the component you're writing represents:type Props = ModalComponentProp<
ModalStackParams,
void,
'PokedexEntryModal',
>
If we reuse our Pokédex example, first we'd need to define our
ModalStackParams
as explained at the end of the previous section. ./types/modals.ts
export interface ModalStackParams {
PokedexEntryModal: {
name: string
type: string
id: number
}
}
And then, our
'PokedexEntryModal'
modal could look like this (granted you've properly set up your declaration file):Hooks
Class
./modals/PokedexEntryModal.tsx
import React from 'react'
import { ModalProps } from 'react-native-modalfy'
type Props = ModalProps<'PokedexEntryModal'>
const PokedexEntryModal = (props: Props) => {
return (
// ...
)
}
export default PokedexEntryModal
./modals/PokedexEntryModal.tsx
import React from 'react'
import { ModalProps } from 'react-native-modalfy'
type Props = ModalProps<'PokedexEntryModal'>
class PokedexEntryModal extends React.Component<Props> {
render() {
return (
// ...
)
}
}
export default PokedexEntryModal
Given that you can reuse the same component for several modals, you can replace that 1st argument with a union type to make everything work.
type Props = ModalProps<'PokedexEntryModal' | 'FavouritePokemonModal'>
Although you'll never manually render
<PokedexEntryModal>
yourself, ModalComponentProp
/ ModalProps
voluntarily expects props types as your modal component could be getting props from some HOCs. ie:Class
./modals/PokedexEntryModal.tsx
// ...
import { connect } from 'react-redux'
// ...
import { ReduxState } from '../types/redux.ts'
interface OwnProps {
favouritePokemon: ReduxState['user']['favouritePokemon']
}
type Props = ModalProps<'PokedexEntryModal', OwnProps>
class PokedexEntryModal extends React.Component<Props> {
//...
}
const mapStateToProps = (state: ReduxState): OwnProps => {
favouritePokemon: state.user.favouritePokemon
}
export default connect(mapStateToProps)(PokedexEntryModal)
Please check out the
ModalComponentProp
/
ModalProps
API reference to have an exhaustive list of what it brings with it.ModalComponentWithOptions
is only meant to be used with Hooks modal components. If you're working with classes, simply use the staticmodalOptions
property as explained below.As we saw in the Configuring a stack guide, you have 3 different ways to provide options to a modal. While the first 2 are type-checked during the modal stack creation, only the 3rd one involves typing
modalOptions
from within the modal component itself. To do so, simply pass your component props to
ModalComponentWithOptions
and you're done! The interface will also directly take care of the fact that you're using it on a component, so no need to use React.FC
with it. ie:Hooks
./modals/PokedexEntryModal.tsx
import React from 'react'
import { ModalComponentWithOptions, ModalProps } from 'react-native-modalfy'
import { ModalStackParams } from '../types/modals.ts'
type Props = ModalProps<'PokedexEntryModal'>
const PokedexEntryModal: ModalComponentWithOptions<Props> = () => {
return (
// ...
)
}
PokedexEntryModal.modalOptions = {
backdropColor = "rebeccapurple"
}
export default PokedexEntryModal
Notice how
ModalComponentWithOptions<Props>
is used right after the modal variable name, not inside the parenthesis of the arrow function!If you're working with a class, you'll just have to directly type the static
modalOptions
property with the same ModalOptions
we used to type our modal stack. eg:Class
./modals/PokedexEntryModal.tsx
import React from 'react'
import {
ModalComponentProp,
ModalOptions
} from 'react-native-modalfy'
import { ModalStackParams } from '../types/modals.ts'
type Props = ModalComponentProp<
ModalStackParams,
void,
'PokedexEntryModal',
>
class PokedexEntryModal extends React.Component<Props> {
static modalOptions: ModalOptions = {
backdropColor: 'rebeccapurple'
}
render() {
return (
// ...
)
}
}
export default PokedexEntryModal
Starting with
v3.5.0
you're now able to omit to provide your equivalent of ModalStackParams
to modalfy's methods. In order to do so:- 1.Declare your own
ModalStackParams
type:
./types/modals.ts
export type ModalStackParams = {
AlertModal: { title: string; message: string };
};
- 2.Create a
react-native-modalfy.d.ts
declaration file. Could be at the same file level wherecreateModalStack
is called and add:
./src/react-native-modalfy.d.ts
import 'react-native-modalfy';
import type { ModalStackParams } './types/modal.types';
declare module 'react-native-modalfy' {
interface ModalfyCustomParams extends ModalStackParams {}
}
- 3.Get full support of types out of the box:
./App.tsx
const AlertModal = () => {
// No need to manually pass ModalStackParams anymore 👇
- const { openModal } = useModal<ModalStackParams>();
+ const { openModal } = useModal();
const openDialog = () => {
// Type checking/autocompleting still works well here 👇
openModal('AlertModal', { title: 'Hello', message: 'Welcome aboard!' });
};
// ...
}
Last modified 3mo ago