Reputation: 343
I am trying to learn typescript with react and I am pretty confused right now, how will I setState of an interface? My code example:
interface Props {
children: ReactNode | ReactNode[];
}
export interface IMovie {
title: string;
video: boolean;
poster_path: string;
}
export const MoviesContext = createContext<IMovie[] | undefined>([]);
export const MoviesProvider = ({ children }: Props): JSX.Element => {
const [movies, setMovies] = useState<IMovie[]>();
return (
<MoviesContext.Provider value={[movies, setMovies]}>
{children}
</MoviesContext.Provider>
);
};
The error I get is "Type (ΙΜovie[] | Dispatch> | undefined>> is not assignable to type IMovie[]"
any other suggestions on what to change on my code welcomed. :)
Upvotes: 13
Views: 36874
Reputation: 30360
The issue here is the value type that your context is defined with is IMovie
, where as the type being passed to your context during rendering is actually an array: [movies, setMovies]
.
One solution would be to define and use a prop interface with your provider, that carries both the movies
state as well as the setter for movies like this:
export interface IMovieProviderProps {
movies? : IMovie,
setMovies : (movies:IMovie) => void,
}
This IMovieProviderProps
interface would then be used to define the shape of your context value, which would provide the means for accessing the movies
state as well as updating it, from outside the provider:
/* Define context with new provider props */
export const MoviesContext = createContext<IMovieProviderProps>(null);
export const MoviesProvider = ({ children }: Props): JSX.Element => {
const [movies, setMovies] = useState<IMovie>();
/* Define provider value. Can be done inline, but I've defined
it here for clarity */
const providerValue : IMovieProviderProps = {
movies,
setMovies
}
/* Pass providerValue to provider */
return (<MoviesContext.Provider value={providerValue}>
{children}
</MoviesContext.Provider>);
};
/* Define a hook for this provider, that allows the provided value
to be consumed */
export const useMovies = () => useContext(MoviesContext);
The useMovies
hook allows the provider value to be accessed from elsewhere in your project, and can be used like this:
const ExampleComponent = () => {
/* Imported useMovies hook defined with provider above, and
extract the movies state, and movies setter base on the shape
of IMovieProviderProps */
const { movies, setMovies } = useMovies();
return <>
{JSON.stringify(movies)}
<button onClick={() => setMovies(null)}>Set Movies</button>
</>
}
A key thing to note now is that the context now exposes an object value with the shape of IMovieProviderProps
rather than an array value (as your code may have been expecting).
Hope that helps!
Upvotes: 20