'How can I correctly initialize the type `Dispatch<SetStateAction<string>>` as a context state?
Overview
I have an app where I want to build some global state between pages in Next.js and I'm using React Context as a solution + TypeScript. I created the context (MusicContext) and provider (Music Provider) where I want to pass the state of the MusicProvider, which includes:
- playlistTitle
- setPlaylistTitle
- songsMatched
- setSongsMatched
- songsMissing
- songsMissing
Question
How can I correctly initialize the type Dispatch<SetStateAction<string>> as a context state? Right now, the issue is coming from here:
setPlaylistTitle: (value: string) => value,
setSongsMatched: (value: Array<ISong>) => value,
setSongsMissing: (value: Array<ISong>) => value,
Error
Type '(value: string) => string' is not assignable to type '() => Dispatch<SetStateAction<string>>'.ts(2322)
musicContext.ts(12, 3): The expected type comes from property 'setPlaylistTitle' which is declared here on type 'IMusicContext'
(property) IMusicContext.setPlaylistTitle: () => Dispatch<SetStateAction<string>>
React Context: MusicContext
// Imports: Dependencies
import { createContext, useContext, Dispatch, SetStateAction } from 'react';
// Imports: TypeScript Types
import { ISong } from '../types/types';
// TypeScript: Music Context
interface IMusicContext {
playlistTitle: string;
songsMatched: Array<ISong>;
songsMissing: Array<ISong>;
setPlaylistTitle: () => Dispatch<SetStateAction<string>>;
setSongsMatched: () => Dispatch<SetStateAction<string>>;
setSongsMissing: () => Dispatch<SetStateAction<string>>;
}
// React Context: Music
export const MusicContext = createContext<IMusicContext>({
// Data
playlistTitle: 'Default Text', // Button Title
songsMatched: [],
songsMissing: [],
// Set State (TODO: HOW DO I INITIALIZE THE SET STATE VALUES???)
setPlaylistTitle: (value: string) => value,
setSongsMatched: (value: Array<ISong>) => value,
setSongsMissing: (value: Array<ISong>) => value,
});
// React Context: useMusicContext
export const useMusicContext = () => useContext<IMusicContext>(MusicContext);
React Context: MusicProvider
// Imports: Dependencies
import { useState } from 'react';
// Imports: React Context
import { MusicContext } from './musicContext';
// Imports: TypeScript Types
import { ISong } from '../types/types';
// TypeScript Type: Props
interface IProps {
value: any;
children: JSX.Element;
}
// React Context: Provider
export const MusicProvider: React.FC<IProps> = ({ value, children }: IProps) => {
// React Hooks
const [playlistTitle, setPlaylistTitle] = useState<string>('');
const [songsMatched, setSongsMatched] = useState<Array<ISong>>([]);
const [songsMissing, setSongsMissing] = useState<Array<ISong>>([]);
return (
<MusicContext.Provider value={{ playlistTitle, songsMatched, songsMissing, setPlaylistTitle, setSongsMatched, setSongsMissing }}>
{children}
</MusicContext.Provider>
);
};
Solution 1:[1]
The direct answer to your question is this: as long as you don't use the context before the MusicContext.Provider is rendered (and there are few cases that would ever be necessary), there's no need to initialize a value by providing an argument to createContext. The reason for this is that whatever value you would have provided will be overwritten by the value provided to the MusicContext.Provider. (You can read more about this in detail in another answer.)
Some of the types in your IMusicContext type need to be modified as well. Here's what those changes would look like:
import {
createContext,
useContext,
Dispatch,
SetStateAction,
} from 'react';
// You didn't show this type in the question information:
type ISong = unknown;
export interface IMusicContext {
playlistTitle: string;
songsMatched: ISong[];
songsMissing: ISong[];
setPlaylistTitle: Dispatch<SetStateAction<string>>;
setSongsMatched: Dispatch<SetStateAction<ISong[]>>;
setSongsMissing: Dispatch<SetStateAction<ISong[]>>;
}
export const MusicContext = createContext<IMusicContext>(undefined as any);
export const useMusicContext = () => useContext(MusicContext);
Any function that you provide in an initialized value won't be of use anyway because there's no state to update yet at that point. If you want to provide an initial value for the properties that are of type Dispatch<SetStateAction<T>>, you can use this function and TypeScript will accept it:
function noop () {}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

