React: How to solve: Property 'children' does not exist on type 'IntrinsicAttributes & Props' - reactjs

I'm trying fetch data from an API and display the data into list of cards in React with typeScript. Since I am new with React in Typescript, not sure how I can solve this error or am I missing something.
This is the error I get:
Type '{ children: string[]; key: number; }' is not assignable to type 'IntrinsicAttributes & Props'.
Property 'children' does not exist on type 'IntrinsicAttributes & Props'.
This is the code:
interface Props {
pokemonItem: PokemonItem;
}
export const PokemonCardList = (props: Props) => {
const { pokemonItem } = props;
const {
id = '',
name = '',
weight = '',
height = '',
abilities = '',
} = pokemonItem;
const [pokemon, setPokemon] = React.useState<PokemonItem[]>([]);
const [loadItems, setLoadItems] = React.useState(API_URL);
const getPokemons = async () => {
setLoading(true);
const response: any = await fetch(loadItems);
const data = await response.json();
setLoadItems(data.next);
setPokemon(data.results[0].name);
setLoading(false);
const getEachPokemon = (result: any) => {
result.forEach(async (element: any) => {
const response = await fetch(
`https:pokeapi.co/api/v2/pokemon/${element.id}`
);
const data = await response.json();
// // setPokemon((currentArrayList) => [...currentArrayList, data]);
pokemon.push(data);
});
};
getEachPokemon(data.results);
await console.log(pokemon);
};
React.useEffect(() => {
return getPokemons();
}, []);
return (
<div>
{pokemon &&
pokemon.map((item, index) => (
<PokemonCard key={index}>
{item.name} {item.height} {item.weight} {item.abilities}
</PokemonCard>
))}
</div>
);
};
Thie pokemonCard component:
interface Props {
pokemonItem: PokemonItem;
}
const PokemonCard = (props: Props) => {
const { pokemonItem } = props;
const {
id = '',
name = '',
weight = '',
height = '',
abilities = '',
} = pokemonItem;
const [imageLoaded, setImageLoaded] = React.useState(false);
const urlImage = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png?raw=true`;
return (
<div imageLoaded={imageLoaded}>
<div
src={urlImage}
onLoad={() => setImageLoaded(true)}
/>
<div>
Name: {name}
Height: {height}
Weight: {weight}
Abilities: {abilities}
</div>
</div>
);
};

Use PropsWithChildren from react:
import React, {Component, PropsWithChildren} from "react";
interface OwnProps {
foo?: BarComponent;
}
// For class component
class MyComponent extend Component<PropsWithChildren<OwnProps>> {
...
}
// For FC
const MyFunctionComponent: FC<PropsWithChildren<Props>> = ({
children,
}) => (...)

According to your definition of the PokemonCard component, you should be passing the pokemonItem as follow:
<PokemonCard pokemonItem={item} key={item.id} />
I have replaced the key prop as it is not recommended to use indexes as keys (see documentation), you could use the item's id instead. And you need to update the prop interface for the PokemonCard component so that the additional key prop doesn't break the validation:
interface Props {
pokemonItem: PokemonItem;
key: string;
}

Try this (add type for you component):
export const PokemonCardList: React.FC<Props> = (props) => {}

import React from "react";
import Paper, { PaperProps } from "#material-ui/core/Paper";
interface TableEmployeeProps extends PaperProps {
// your props
}
const TableEmployee: React.FC<TableEmployeeProps> = (props) => {
return (
<Paper {...props}> </Paper>
}

Related

react hook with generic interface

i'm trying to write a hook to be used by two of my components-datepicker and dateRangePicker.
Two components extend antd datepicker and rangerpicker, and have the same way of handling states. DatePicker is for selecting a single date and Rangepicker for range of date.
I am trying to extract common logic out of these components and make a custom hook.
Here's my DateRangePicker. Date Picker Looks almost the same.
import { useRef } from "react";
import { DateRangePickerProps } from "./types";
import * as S from "./styles";
import { ReactComponent as CalenderIcon } from "#assets/images/calender.svg";
import Footer from "./Footer";
import { useDatePicker } from "./hooks";
function DateRangePicker({
isError = false,
withFooter = false,
showNextPage = false,
format = "DD/MM/YYYY",
onChange,
defaultValue,
placeholder = ["From", "To"],
...props
}: DateRangePickerProps) {
const wrapperRef = useRef(null);
const {
confirmedValue,
isOpen,
handleOpen,
handleKeyDown,
handleChange,
handleConfirm,
handleDismiss
} = useDatePicker<DateRangePickerProps>(
defaultValue,
onChange,
withFooter,
wrapperRef
); // calling custom hook
return (
<S.Wrapper ref={wrapperRef}>
<S.RangePicker
{...props}
value={props.value || confirmedValue?.date}
open={isOpen}
onOpenChange={handleOpen}
onKeyDown={handleKeyDown}
onChange={handleChange}
isError={isError}
separator={<S.Separator>-</S.Separator>}
suffixIcon={<CalenderIcon />}
format={format}
getPopupContainer={triggerNode => triggerNode}
withFooter={withFooter}
renderExtraFooter={() =>
withFooter && (
<Footer onConfirm={handleConfirm} onDismiss={handleDismiss} />
)
}
showNextPage={showNextPage}
placeholder={placeholder}
/>
</S.Wrapper>
);
}
export default DateRangePicker;
And here's my Custom hook.
I've used Generic parameter which could either be 'DateRangePickerProps' or 'DatePickerProps'
import { useState, useEffect, useRef, RefObject } from "react";
import { DateRangePickerProps, DatePickerProps } from "./types";
type Event = MouseEvent | TouchEvent;
interface SelectedDate<T extends DateRangePickerProps | DatePickerProps> {
date: T['value'] | null
dateString: T["dateString"] | null;
}
export function useDatePicker<T extends DateRangePickerProps | DatePickerProps>(
defaultValue: T['defaultValue'],
onChange: T['onChange'],
withFooter: boolean,
wrapperRef: RefObject<HTMLElement>
) {
const [isOpen, setIsOpen] = useState<boolean>(false);
// confirmedRange, pendingRange 타입 일치 시키기
const [confirmedValue, setConfirmedValue] = useState<SelectedDate<T>>({
date: defaultValue ?? null,
dateString: null
});
const [pendingValue, setPendingValue] = useState<SelectedDate<T>>({
date: null,
dateString: null
});
const sync = useRef(true);
useEffect(() => {
// invoke user-passed onChange
if (confirmedValue.dateString !== null && confirmedValue && !sync.current) {
onChange?.(confirmedValue.date, confirmedValue.dateString);
sync.current = true;
}
}, [confirmedValue, onChange]);
useEffect(() => {
// prevent previous pendingValue from taking over
setPendingValue({ date: null, dateString: null });
}, [isOpen]);
useOnClickOutside(wrapperRef, () => {
if (withFooter) setIsOpen(false);
});
const handleChange: T['onChange'] = (date, dateString) => {
// I get error here; Parameter 'date' implicitly has an 'any' type.ts(7006)
if (withFooter) setPendingValue({ date, dateString });
else {
sync.current = false;
setConfirmedValue({ date, dateString });
}
};
const handleOpen = (internalOpenState: boolean) => {
if (withFooter && !internalOpenState) return;
setIsOpen(internalOpenState);
};
const handleConfirm = () => {
// prevent null(incomplete date range) from taking over
if (pendingValue) {
setConfirmedValue(pendingValue);
sync.current = false;
}
setIsOpen(false);
};
const handleDismiss = () => {
setIsOpen(false);
};
const handleKeyDown: T["onKeyDown"] = key => {
if (key.code === "Escape") {
setIsOpen(false);
}
};
return {
confirmedValue,
isOpen,
handleOpen,
handleKeyDown,
handleChange,
handleConfirm,
handleDismiss
};
}
DateRangePickerProps and DatePickerProps share common properties but with slight difference. For example, 'onChange' is gets parameters of (moment, string) in datePicker, whereas ([moment, moment], [string, string]) is valid in rangePicker.
I'm using following types
import type { DatePickerProps as AntDatePickerProps } from "antd";
import { RangePickerProps as AntRangePickerProps } from "antd/lib/date-picker";
export interface CustomProps {
isError?: boolean;
withFooter?: boolean;
showNextPage?: boolean;
}
export type DatePickerProps = AntDatePickerProps &
CustomProps & { dateString: string };
export type DateRangePickerProps = AntRangePickerProps &
CustomProps & { dateString: [string, string] };
export interface ButtonProps {
size?: "small" | "medium" | "large";
$type?: "primary" | "secondary";
children?: React.ReactNode;
}
export interface FooterProps {
onDismiss: () => void;
onConfirm: () => void;
}
It seems like my approach of generic interface isn't well understood by typescript.
My understanding is that typescript can't figure out which of interface will be passed.
I've left another question regarding this and got an answer, but couldn't implement it.
Generic Function and index access
Can anyone come up with better and error-free approach of dealing with this?
Thanks in advance.

How to solve this Typescript type problem in React

I'm trying to implement the data type returned from the API I'm using. The total_page and total_results are marked as an error. Looks like I need to implement some type. I can't understand how to solve this problem this type requirement. The message is:
(property) React.ProviderProps.value: MoviesContextData
The type '{ movies: MovieData[]; pageNumber: number; setPageNumber: Dispatch<SetStateAction>; }' does not have the following properties of type 'MoviesContextData': total_pages, total_results ts(2739)
index.d.ts(327, 9): The expected type comes from the 'value' property, which is declared here in the type 'IntrinsicAttributes & ProviderProps'"
import {
useContext,
createContext,
useEffect,
useState
} from "react"
import { MoviesProviderProps } from "../interfaces/props"
import { MovieData, MoviesContextData } from "../interfaces/types"
import axios from "axios"
import { POPULAR_BASE_URL } from "../services/api"
const MoviesContext = createContext<MoviesContextData>(
{} as MoviesContextData
)
export const MoviesProvider = ({ children }: MoviesProviderProps) => {
const [movies, setMovies] = useState<MovieData[]>([])
const [pageNumber, setPageNumber] = useState(1)
const [totalResults, setTotalResults] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
const nextPage = ({ pageNumber }: MoviesContextData) => {
axios.get(`${POPULAR_BASE_URL}&page=${pageNumber}`)
}
useEffect(() => {
axios.get(`${POPULAR_BASE_URL}&page=${pageNumber}`)
.then(response => {
setMovies(response.data.results)
})
}, [])
return (
<MoviesContext.Provider value={{ movies, pageNumber, setPageNumber }}>
{children}
</MoviesContext.Provider>
)
}
export const useMovies = () => {
const context = useContext(MoviesContext)
return context
}
Types:
export type MoviesContextData = {
movies: MovieData[];
pageNumber: number;
setPageNumber: (prevState: number) => void;
total_pages: number;
total_results: number;
}
export type MovieData = {
id: number;
backdrop_path: string;
poster_path: string;
title: string;
release_date: string;
vote_average: number;
}
You define MoviesContextData as:
export type MoviesContextData = {
movies: MovieData[];
pageNumber: number;
setPageNumber: (prevState: number) => void;
total_pages: number;
total_results: number;
}
And then you provide this value for that type:
<MoviesContext.Provider value={{ movies, pageNumber, setPageNumber }}>
total_pages and total_results are required properties, but are missing in the context provider's value.
That's what this portion of that error message is trying to tell you:
does not have the following properties of type 'MoviesContextData': total_pages, total_results ts(2739)
To fix it you need to provide those properties with something like:
<MoviesContext.Provider value={{
movies,
pageNumber,
setPageNumber,
total_pages: Math.ceil(totalResults / numberOfResultsPerPage),
total_results: totalResults
}}>
Or make the properties optional:
export type MoviesContextData = {
// other properties...
total_pages?: number;
total_results?: number;
}

How to add static props type for ForwardRefExoticComponent

I have a Modal FC, I need to add two static methods show and hide to it. I have no idea how to add the ModalStaticProps type for the component. So that I can assign show and hide to Modal directly without using type assertion.
import React, { ReactNode, RefAttributes } from 'react';
interface ModalProps {
title: ReactNode;
}
interface ModalStaticProps {
show(): void;
hide(): void;
}
const Modal: React.ForwardRefExoticComponent<ModalProps & RefAttributes<HTMLDivElement>> = React.forwardRef<
HTMLDivElement,
ModalProps
>(({ title }: ModalProps, ref) => {
return <div ref={ref}>{title}</div>;
});
// I want to this after add `ModalStaticProps` type
// Modal.show = () => { };
// Modal.hide = () => { };
// Not this
const ModalComponent = Modal as React.ForwardRefExoticComponent<ModalProps & RefAttributes < HTMLDivElement >> & ModalStaticProps
ModalComponent.show = () => { };
ModalComponent.hide = () => { };
function Consumer() {
return <div onClick={() => ModalComponent.show()} />
}
TypeScript Playground
It is doable.
See Properties declarations on functions. It is possible to do, in a natural way, but it might break your ref types.
Hence, I decided just use Object.assign
See example:
import React, { ReactNode, RefAttributes, ForwardRefExoticComponent } from 'react';
interface ModalProps {
title: ReactNode;
}
interface ModalStaticProps {
show(): void;
hide(): void;
}
const STATIC_PROPS: ModalStaticProps = {
show: () => { },
hide: () => { }
}
const withStaticProps = <T,>(
forwarded: ForwardRefExoticComponent<ModalProps & RefAttributes<HTMLDivElement>>,
staticProps: T
) => Object.assign(forwarded, staticProps)
const Modal = React.forwardRef<
HTMLDivElement,
ModalProps
>(({ title }: ModalProps, ref) => <div ref={ref}>{title}</div>)
const ModalComponent = withStaticProps(Modal, STATIC_PROPS)
function Consumer() {
return <div onClick={() => ModalComponent.show()} />
}
Playground
See my question regarding this problem

Type Children Element is missing properties with Nextjs + React Context with TypeScript

Using Reacts Context with Nextjs and TypeScript is showing a vague error wrapping context around the _app.tsx.
Event though I am passing in the values to the Context Provider it's giving me the error:
"Type '{ children: Element; }' is missing the following properties from type 'ContextProps': capturedPokemons, catchPokemon, releasePokemon"
Here is my _app.tsx
function MyApp({ Component, pageProps }: AppProps) {
return (
<CaughtProvider>
<Component {...pageProps} />
</CaughtProvider>
);
}
Here is the Context:
type PokemonProps = {
number: string;
name: string;
image: string;
};
type ContextProps = {
capturedPokemons: PokemonProps[];
catchPokemon: (newPokemon: PokemonProps[]) => void;
releasePokemon: (id: string) => void;
};
const CaughtContext = createContext<ContextProps>({
capturedPokemons: [],
catchPokemon: () => undefined,
releasePokemon: () => undefined,
});
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>
);
};
The app works fine as expected and as I'm aware this is how it's done in plain React/JS without TypeScript.
You need to have a separate type for the CaughtProvider
type CaughtProviderProps = {
children: React.ReactNode
}
and use it as
CaughtProvider: React.FC<CaughtProviderProps>
ContextProps is for your context value so its not right to use it for CaughtProvider . CaughtProvider is just a component which takes the children prop . So its better to have a separate type for it .

React and TypeScript—which types for an Axios response?

I am trying to present a simple user list from an API which returns this:
[{"UserID":2,"FirstName":"User2"},{"UserID":1,"FirstName":"User1"}]
I do not understand fully how to handle Axios responses with types. The TypeScript error is
Type '{} | { id: number; firstName: string; }' is not assignable to type 'IntrinsicAttributes & UserListProps & { children?: ReactNode; }'.
Property 'items' is missing in type '{}' but required in type 'UserListProps'.
from the <UserList /> element in the Users.tsx file below. Is my User interface wrong?
import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios, {AxiosResponse} from 'axios';
interface User {
id: number;
firstName: string;
}
const Users: React.FC = (props) => {
const [users, setUserList] = useState<User>();
useEffect(() => {
// Use [] as second argument in useEffect for not rendering each time
axios.get('http://localhost:8080/admin/users')
.then((response: AxiosResponse) => {
console.log(response.data);
setUserList( response.data );
});
}, []);
return (
<Fragment>
<UserList {...users} />
</Fragment>
);
};
export default Users;
Below is my UserList.tsx.
import React, {Fragment } from 'react';
interface UserListProps {
items: {id: number, firstName: string}[];
};
const UserList: React.FC<UserListProps> = (props) => {
return (
<Fragment>
<ul>
{props.items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
{/* not call delete function, just point to it
// set this to null in bind() */}
</li>
))}
</ul>
</Fragment>
);
};
export default UserList;
There is generic get method defined in axios/index.d.ts
get<T = never, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig<T>): Promise<R>;
Example
interface User {
id: number;
firstName: string;
}
axios.get<User[]>('http://localhost:8080/admin/users')
.then(response => {
console.log(response.data);
setUserList( response.data );
});
I think you are passing list the wrong way to child component.
const [users, setUserList] = useState<User[]>([]);
<UserList items={users} />
interface UserListProps {
items: User[];
};
const UserList: React.FC<UserListProps> = ({items}) => {
return (
<Fragment>
<ul>
{items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
</li>
))}
</ul>
</Fragment>
);
};
You need to provide a type argument when calling axios.get if you do not want Axios to infer the type for the value response as any.
And you are passing an incorrect type argument when you useState to create the array of users.
The correct way
interface User {
id: number;
firstName: string;
}
// Initialized as an empty array
const [users, setUserList] = useState<User[]>([]); // 'users' will be an array of users
For example,
import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios from 'axios';
interface User {
id: number;
firstName: string;
}
// You can export the type TUserList to use as -
// props type in your `UserList` component
export type TUserList = User[]
const Users: React.FC = (props) => {
// You can also use User[] as a type argument
const [users, setUserList] = useState<TUserList>();
useEffect(() => {
// Use [] as a second argument in useEffect for not rendering each time
axios.get<TUserList>('http://localhost:8080/admin/users')
.then((response) => {
console.log(response.data);
setUserList(response.data);
});
}, []);
return (
<Fragment>
<UserList {...users} />
</Fragment>
);
};
export default Users;
If you choose to export the type type TUserList = User[], you can use it in your UserList component as the type for props. For example,
import React, {Fragment } from 'react';
import { TUserList } from './Users';
interface UserListProps {
items: TUserList // Don't have to redeclare the object again
};
const UserList: React.FC<UserListProps> = (props) => {
return (
<Fragment>
<ul>
{props.items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
{ /* Do not call the delete function. Just point
to it. Set this to null in bind(). */}
</li>
))}
</ul>
</Fragment>
);
};
export default UserList;

Resources