How to Create Render Props With Functional React Components
You can find the working example for this lesson in the
code/03-react-piano/03.15-functional-render-props
folder.
Inside the src/adapters/Soundfont
directory create a file called SoundfontProvider.ts
. Add the necessary imports:
xxxxxxxxxx
import {
ReactElement,
FunctionComponent,
useState,
useEffect,
useRef,
useCallback
} from "react"
import Soundfont, { InstrumentName, Player } from "soundfont-player"
import { MidiValue } from "../../domain/note"
import { Optional } from "../../domain/types"
import {
AudioNodesRegistry,
DEFAULT_INSTRUMENT
} from "../../domain/sound"
Declare the component props:
xxxxxxxxxx
type ProvidedProps = {
loading: boolean
play(note: MidiValue): Promise<void>
stop(note: MidiValue): Promise<void>
}
​
type ProviderProps = {
instrument?: InstrumentName
AudioContext: AudioContextType
render(props: ProvidedProps): ReactElement
}
We would require an optional instrument
prop to specify which instrument we want to load, and an AudioContext
to utilize. Most importantly, we would need a render
prop that is a function that takes ProvidedProps
as an argument and returns a ReactElement
. ProvidedProps
is a type with values that we would provide to the outside world.
These are the same values we provided earlier with the useSoundfont()
hook but without load()
and current
. We don't need them because we encapsulate the loading of sounds inside our provider. A current instrument now arrives from the outside via the instrument
prop.
Also, we don't return them as a function result; but instead, we pass them as a render
function argument. Thus, the usage of our new provider would look like this:
xxxxxxxxxx
function renderKeyboard({
play,
stop,
loading
}: ProvidedProps): ReactElement {
return <Keyboard play={play} stop={stop} loading={loading} />
}
​
/** ...And we would use it like:
* <SoundfontProvider
* AudioContext={AudioContext}
* instrument={instrument}
* render={renderKeyboard}
* />
*/
When we are okay with the API of our new provider, we can start implementing it. A type signature of this provider would be like this:
xxxxxxxxxx
export const SoundfontProvider: FunctionComponent<ProviderProps> = ({
AudioContext,
instrument,
render
}) => {
// ...
}
We explicitly say that this is a FunctionComponent
that accepts ProviderProps
.
All the work with the internal state would be the same as it was in the useSoundfont()
hook, except that we add loading and reloading sounds when the instrument
prop is being changed.
This lesson preview is part of the Fullstack React with TypeScript Masterclass course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack React with TypeScript Masterclass with a single-time purchase.
