Home and GetServerSideProps type error in Nextjs with Typescript - reactjs

I'm struggling with getting my first Nextjs project with Typescript and next-firebase-auth package. I want to call the API in getserversideprops, and return the value to the main component, in this case, the index page.
Here is how it looks right now
import {
AuthAction,
useAuthUser,
withAuthUser,
withAuthUserTokenSSR,
} from "next-firebase-auth";
import FullPageLoader from "../components/layout/FullPageLoader";
import SignUp from './signup/index';
import {useEffect} from 'react';
import {useRouter} from 'next/router';
import { NextPage } from "next";
type PageProps = {
profile: boolean | {
username: string,
name: string,
surname: string
}
}
const Home: NextPage<PageProps> = (props) => {
const AuthUser = useAuthUser()
const router = useRouter()
useEffect(() => {
if (props.profile) {
router.push(`/advisor/${AuthUser.id}/profile`)
}
}, [AuthUser.id, props.profile, router])
return (
<>
<SignUp />
</>
);
};
export const getServerSideProps = withAuthUserTokenSSR({
whenUnauthed: AuthAction.REDIRECT_TO_LOGIN,
})(async ({AuthUser}:any) => {
const token = await AuthUser.getIdToken()
const response = await fetch(`htpp://localhost:3001/profile/${AuthUser.id}/get-profile`, {
method: 'GET',
headers: {
Authorization: token || ''
}
})
if (response.status !== 200) {
const _props: PageProps = {profile: false}
return { props: _props }
}
const profile = await response.json()
const _props: PageProps = {profile}
return {
props: _props
}
})
export default withAuthUser({
whenUnauthedBeforeInit: AuthAction.SHOW_LOADER,
whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN,
LoaderComponent: FullPageLoader,
})(Home);
And the compiler throws this error
./src/pages/index.tsx:74:4
Type error: Argument of type 'FunctionComponent<PageProps> & { getInitialProps?(context: NextPageContext): PageProps | Promise<PageProps>; }' is not assignable to parameter of type 'ComponentType<unknown>'.
Type 'FunctionComponent<PageProps> & { getInitialProps?(context: NextPageContext): PageProps | Promise<PageProps>; }' is not assignable to type 'FunctionComponent<unknown>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'unknown' is not assignable to type 'PageProps'.
72 | whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN,
73 | LoaderComponent: FullPageLoader,
> 74 | })(Home);
| ^
75 |
I don't understand why he wants a ComponentType<unknow> because type is declared. Tried also to declare directly ({profile}:any)but throws a similar error. How I can get this to work with Typescript?

Finally solved with the required utility type ?
type PageProps = {
profile?: boolean | {
username: string,
name: string,
surname: string
}
}
and also the return type of the exported Home function
export default withAuthUser<PageProps>({
whenUnauthedBeforeInit: AuthAction.SHOW_LOADER,
whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN,
LoaderComponent: FullPageLoader,
})(Home);

Related

how can i give type in getServerSideProps of Nextjs with typescript?

I'm using NextJs + TypeScript to make a little clone project, but I got a problem with type in getServerSideProps.
As you can see, in getServerSideProps, I am fetching data using with context.query.
But some error message is not fixed and I don't understand why that error appears.
The error message is this.
Type 'string[]' cannot be used as an index type.ts(2538)
Type 'undefined' cannot be used as an index type.ts(2538)
const genre: string | string[] | undefined
How can I fix this type problem?
import Head from "next/head";
import Nav from "../components/Nav/Nav";
import Header from "../components/Header/Header";
import Results from "../components/Results/Results";
import requests from "../utils/requests";
import { GetServerSideProps } from "next";
type HomeProps = {
results: {}[];
};
export default function Home({ results }: HomeProps) {
console.log(results);
return (
<div>
<Results results={results} />
</div>
);
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const genre = context.query.genre
const response = await fetch(
`https://api.themoviedb.org/3${
requests[genre]?.url {/*this is problem line*/}
|| requests.fetchTopRated.url
}`
);
const data = await response.json();
return {
props: { results: data.results },
};
};
You can use like this;
type PageProps = {
isAuthanticated: boolean,
categories?: CategoryType[]
}
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
const _props: PageProps = {
isAuthanticated: auth,
categories: data.results
}
return { props: _props }
};
const Category: NextPage<PageProps> = (props) => {
return(
...
)
};
Since the type of genre can be string or string[] (or undefined), it can not be used to index requests without being narrowed down to string via the use of an if statement:
if (typeof genre === 'string') {
// Typescript now knows it is a string
const response = await fetch(
`https://api.themoviedb.org/3${
requests[genre]?.fetchTopRated.url {/*this is problem line*/}
|| requests.fetchTopRated.url
}`
);
const data = await response.json();
return {
props: { results: data.results },
};
} else if (typeof genre == 'object'){
// handle case where it is an array
} else {
// genre is undefined
}
When you receive params via context, the value could be either string or string[] (or undefined) so you need to cast. It could be a single genre or multiple genres in the URL.
?genre=film or ?genre=film&genre=music
For you case, simply cast as string:
const genre = context.query.genre as string;
UPDATE
As per your comments, the first issue that you raised in the question was actually about casting to string as above.
The second issue, which you should not actually be seeing and must be a TS or module config issue, is related to trying to accessing a key as string by index on your vanilla object exported from "../utils/requests";
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ fetchTrending: { title: string; url: string; }; ...
Your data object has literal key names:
// ../utils/requests
export default {
fetchTrending: {
title: "Trending",
url: /trending/all/week?api_key=${API_KEY}&language=en-US,
},
fetchTopRated: {
title: "Top Rated",
url: /movie/top_rated?api_key=${API_KEY}&language=en-US,
},
};
Rather define the type like this:
export interface IRequest {
[name: string]: {
title: string;
url: string;
};
}
const data: IRequest = {
fetchTrending: {
title: "Trending",
url: `/trending/all/week?api_key=${API_KEY}&language=en-US1`
},
fetchTopRated: {
title: "Top Rated",
url: `/movie/top_rated?api_key=${API_KEY}&language=en-US`
}
};
export default data;
or you could use a Record to have strongly typed keys:
type RequestNames = "fetchTrending" | "fetchTopRated";
export const records: Record<
RequestNames,
{
title: string;
url: string;
}
> = {
fetchTrending: {
title: "Trending",
url: `/trending/all/week?api_key=${API_KEY}&language=en-US1`
},
fetchTopRated: {
title: "Top Rated",
url: `/movie/top_rated?api_key=${API_KEY}&language=en-US`
}
};

TypeScript/React - destructuring from useContext throws an error

I'm trying to work with the useContext hook in a TypeScript/React app. So I created a context object with createContext and passed a sampleObject to the Provider's value prop. When I then try to destructure the properties from useContext, TypeScript throws an error for each property:
Property 'name' does not exist on type 'AppContextInterface | null'.
Property 'author' does not exist on type 'AppContextInterface | null'.
Property 'url' does not exist on type 'AppContextInterface | null'.
Here's the code:
interface AppContextInterface {
name: string;
author: string;
url: string;
}
const AppCtx = React.createContext<AppContextInterface | null>(null);
const sampleObject: AppContextInterface = {
name: 'Using React Context in a TypeScript App',
author: 'thehappybug',
url: 'http://www.example.com'
}
const App = () => (
<AppCtx.Provider value={sampleObject}>
<PostInfo />
</AppCtx.Provider>
);
const PostInfo = () => {
const appContext = useContext(AppCtx);
// typescript throws the errors on the next line
const { name, author, url } = appContext;
return (
<div>
Name: {name}, Author: {author}, url: {url}
</div>
);
}
Not sure what the problem is...
Since appContext can be null, you need to handle that case
const PostInfo = () => {
const appContext = useContext(AppCtx);
if (!appContext) return null;
const { name, author, url } = appContext;
return (
<div>
Name: {name}, Author: {author}, url: {url}
</div>
);
}

Function and data not appearing on type ContextProps. React Context with TypeScript

I'm getting some vague errors when using React Context with TypeScript to pass things down.
The app works well and displays everything it needs to and the functions work yet TypeScript is yelling at me.
Heres my Context being created.
import React, { createContext, useContext, useEffect, useState } from 'react';
type PokemonProps = {
number: string;
name: string;
image: string;
};
type ContextProps = {
capturedPokemons: PokemonProps[];
catchPokemon: (newPokemon: PokemonProps[]) => void;
releasePokemon: (id: string) => void;
};
const CaughtContext = createContext<ContextProps | null>(null);
export const useCaught = () => useContext(CaughtContext);
export const CaughtProvider: React.FC<ContextProps> = ({ children }) => {
const [capturedPokemons, setCapturedPokemons] = useState<any>([]);
const catchPokemon = (newPokemon: PokemonProps[]) => {
if (capturedPokemons.length >= 6) {
alert('You cannot carry any more Pokemon.');
return;
}
const alreadyCaptured = capturedPokemons.some(
(p: PokemonProps) => p.name === newPokemon[0].name
);
if (alreadyCaptured) {
alert('you already have that pokemon');
return;
}
if (window.confirm('Capture Pokemon')) {
setCapturedPokemons([...capturedPokemons, ...newPokemon]);
}
};
return (
<CaughtContext.Provider
value={{ catchPokemon, capturedPokemons, releasePokemon }}>
{children}
</CaughtContext.Provider>
);
};
When using the above code to use the functions or state in my app for example const { catchPokemon } = useCaught(); I get and error "Property 'catchPokemon' does not exist on type 'ContextProps | null'." How does catchPokemon not exist on type ContextProps when I have created the type here:
type ContextProps = {
capturedPokemons: PokemonProps[];
catchPokemon: (newPokemon: PokemonProps[]) => void;
releasePokemon: (id: string) => void;
};
Once again everything works as expected but Typescript is telling me something is wrong with vague solutions.

React Navigation Error - TypeScript - This Expression Is Not Callable

I'm trying to find the best practice for this typescript issue I'm experiencing when I upgraded to React Navigation 5. The error I'm getting is
This expression is not callable.
Each member of the union type '{ <RouteName extends "Stack1Screen1" | "Home">(...args:
undefined extends SampleParamList1[RouteName] ? [RouteName] | [RouteName,
SampleParamList1[RouteName]] : [...]): void; <RouteName extends "Stack1Screen1" |
"Home">(route: { ...; } | { ...; }): void; } | { ...; }' has signatures, but none of those
signatures are compatible with each other.ts(2349)
Here is the code I'm essentially using:
import { StackScreenProps } from '#react-navigation/stack';
export type SampleParamList1 = {
Stack1Screen1: undefined;
Home: undefined;
};
export type SampleParamList2 = {
Stack2Screen2: undefined;
Home: undefined;
};
type Props =
| StackScreenProps<SampleParamList1, 'Stack1Screen1'>
| StackScreenProps<SampleParamList2, 'Stack2Screen2'>;
const ThisScreen: React.FC<Props> = ({ navigation, route }) => {
const navToHome = () => navigation.navigate('Home');
};
Hovering over the navigation.navigate('Home') function displays the error message.
Any ideas on how to solve this? Thanks! :)
I used something like this.
export type RandomParamList = {
ScreenRandom1: undefined;
ScreenRandom2: undefined | { screen: string }; // arguments
ScreenRandom3: undefined | { screen?: string, params: { param1: string } };
} ;
export type HomeParamList = {
HomeScreen1: undefined;
HomeScreen2: undefined | { screen: string };
HomeScreen3: undefined | { screen?: string, params: { param1: string } };
HomeScreen4: undefined;
HomeScreen5: undefined | { screen: string };
} & RandomParamList;
export type HomeNavProps<T extends keyof HomeParamList> = {
navigation: StackNavigationProp<HomeParamList, T>;
route: RouteProp<HomeParamList, T>;
};
const ThisScreen: React.FC<HomeNavProps> = ({ navigation, route }) => {
const navToHome = () => navigation.navigate('HomeScreen1');
const navToHome = () => navigation.navigate('Home'); // you will get error that Home is not allowed
};

Next.js with-redux example for TypeScript

I'm building a Next.js app. I'm trying to connect it to Redux. I tried following the official with-redux example, but since that's written in JavaScript, it doesn't translate well.
So I started creating types for everything. Here is what I have so far.
import { NextPage } from 'next';
import React from 'react';
import { Provider } from 'react-redux';
import { RootState } from '../../redux/root-reducer';
import { initializeStore, Store } from '../../redux/store';
let reduxStore: Store;
const getOrInitializeStore = (initialState?: RootState): Store => {
// Always make a new store if server, otherwise state is shared between requests
if (typeof window === 'undefined') {
return initializeStore(initialState);
}
// Create store if unavailable on the client and set it on the window object
if (!reduxStore) {
reduxStore = initializeStore(initialState);
}
return reduxStore;
};
type WithRedux = <P extends RootState>(
PageComponent: NextPage,
options: { ssr?: boolean },
) => React.Component<P>;
export const withRedux: WithRedux = (PageComponent, { ssr = true } = {}) => {
const WithRedux = ({ initialReduxState, ...props }) => {
const store = getOrInitializeStore(initialReduxState);
return (
<Provider store={store}>
<PageComponent {...props} />
</Provider>
);
};
// Set the correct displayName in development
if (process.env.NODE_ENV !== 'production') {
const displayName =
PageComponent.displayName || PageComponent.name || 'Component';
WithRedux.displayName = `withRedux(${displayName})`;
}
if (ssr || PageComponent.getInitialProps) {
WithRedux.getInitialProps = async (
context: NextPageContext,
): Promise<{}> => {
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrInitializeStore();
// Provide the store to getInitialProps of pages
context.reduxStore = reduxStore;
// Run getInitialProps from HOCed PageComponent
const pageProps =
typeof PageComponent.getInitialProps === 'function'
? await PageComponent.getInitialProps(context)
: {};
// Pass props to PageComponent
return {
...pageProps,
initialReduxState: reduxStore.getState(),
};
};
}
return WithRedux;
};
As you can see I,
Deleted the check that warns you if you try to connect the HOC to Next.js App component. TypeScript would catch that earlier and complaint that this check would always evaluate to false.
I imported the Store and RootState types to type the return types of getOrInitializeStore.
I tried typing the withRedux HOC. My current types throw errors for withRedux. There are also errors for the initialReduxState argument for the WithRedux component and the reduxStore property for context.
Here are the errors.
withRedux
Type '<P extends { readonly [$CombinedState]?: undefined; }>(PageComponent: NextComponentType<NextPageContext, {}, {}>, { ssr }?: { ssr?: boolean | undefined; }) => { ...; }' is not assignable to type 'WithRedux'.
Type '{ ({ initialReduxState, ...props }: { [x: string]: any; initialReduxState: any; }): Element; displayName: string; getInitialProps(context: any): Promise<{ initialReduxState: unknown; }>; }' is missing the following properties from type 'Component<P, {}, any>': context, setState, forceUpdate, render, and 3 more.
WithRedux
Binding element 'initialReduxState' implicitly has an 'any' type
reduxStore
Property 'reduxStore' does not exist on type 'NextPageContext'.
How can I resolve these errors and get TypeScript to compile?

Resources