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

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 .

Related

Portal container is not type correct

Hello I have a problem with the portal. I am using react with Typescript. The problem is that I create an element in the use-portal, but React wants the ReactNode. How can I create some walkaround except using "any"?
I get an error
Argument of type 'Element' is not assignable to parameter of type 'ReactNode'.
Type 'Element' is missing the following properties from type 'ReactPortal': key, type, props
Portal.tsx
import { createPortal } from "react-dom";
import { usePortal } from "./use-portal";
interface PortalProps {
rootId: string;
children: JSX.Element;
}
const Portal: React.FC<PortalProps> = ({ rootId, children }) => {
const portalContainer = usePortal(rootId);
return createPortal(portalContainer, children);
};
export default Portal;
use-portal.tsx
const createRootElement = (id: string): Element => {
const rootContainer = document.createElement("div");
rootContainer.setAttribute("id", id);
return rootContainer;
};
const addRootElement = (rootElem: Element): void => {
const elements = document.querySelectorAll("body > div");
if (elements.length > 0) {
const lastElement = elements[elements.length - 1];
document.body.insertBefore(rootElem, lastElement);
} else {
document.body.append(rootElem);
}
};
export const usePortal = (id: string): Element => {
const existingElement = document.getElementById(id);
const portalElement = existingElement ?? createRootElement(id);
if (!existingElement) {
addRootElement(portalElement);
}
return portalElement;
};
The problem was solved, the problem was the order, I had to swap children and portalContainer in createPortal. The correct Portal component:
const Portal: React.FC<PortalProps> = ({ rootId, children }) => {
const portalContainer = usePortal(rootId);
return createPortal(children, portalContainer);
};
Rookie mistake :)

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.

TypeScript erros passing values into Context.Provider

I'm getting errors using useContext and React.
My app does work fine but the errors continue. It's as if my ContextProps are not in scope. Yet as far as I can see I am adding the Props to the functional component.
Currently
type PokemonProps = {
number: string;
name: string;
image: string;
};
type ContextProps = {
capturedPokemons: PokemonProps;
catchPokemon: (newPokemon: PokemonProps) => void;
releasePokemon: (id: string) => void;
totalCaptured: number;
newPokemon: PokemonProps;
};
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: ContextProps) => {
// Stuff happens here
};
useEffect(() => {
const localPokemons = localStorage.getItem('capturedPokemons');
if (localPokemons) {
setCapturedPokemons(JSON.parse(localPokemons));
}
}, []);
useEffect(() => {
localStorage.setItem('capturedPokemons', JSON.stringify(capturedPokemons));
}, [capturedPokemons]);
return (
<CaughtContext.Provider
/* ERROR =============
Type '{ catchPokemon: (newPokemon: ContextProps) => void; capturedPokemons: any; releasePokemon: () => void; }' is not assignable to type 'ContextProps'.
Object literal may only specify known properties, and 'catchPokemon' does not exist in type 'ContextProps'
================ */
// This is what displays the above error
value={{ catchPokemon, capturedPokemons, releasePokemon }}>
{children}
</CaughtContext.Provider>
);
};
Forgive me simple I'm learning TypeScript as I go.

React component children not rendering

I have a React component that iterates over an array in its refs.current on each render and returns an array of JSX elements that correspond to the refs.current array.
I'm treating the array in refs.current as a pseudo-state. Anyways it works fine usually, but I get this weird case where it will just not update the children components.
I even wrote a console log in the return statement, and all the JSX it is spitting back is what should be rendered after the component settles.
export type ContextItem = {
name: string,
color?: string,
callback: ContextItemCallback,
keybind?: string,
checkbox?: () => boolean,
refs?: MainContextItemRefs,
setRefs?: (action: MainContextItemRefsAction, ...args: any) => void,
};
export type ContextItemCallback = (e: React.MouseEvent | KeyboardEvent, refs: WindowRefs,
setRefs: (action: WindowRefsAction, ...args: any) => void) => void;
export interface Props {
windowRefs: WindowRefs;
setWindowRefs: (action: WindowRefsAction, ...args: any) => void
menuTitle: string;
}
export interface Refs {
ref: React.MutableRefObject<HTMLDivElement>;
items: ContextItem[];
visible: boolean;
height: number;
position: V2 | NodePhysics | DSPhysics;
offset: V2;
}
export const initRefs: Refs = {
ref: React.createRef() as React.MutableRefObject<HTMLDivElement>,
items: [],
}
export enum RefsAction {
SET_ITEMS = "SET_ITEMS",
SET_REFS = "SET_REFS",
}
export const ContextMenu: React.FC<Props> = ({ windowRefs, setWindowRefs, menuTitle }) => {
let { state: appState, setState: setAppState, refs: appRefs } = React.useContext<AppContextType>(AppContext); //eslint-disable-line
let {current: refs } = React.useRef({...windowRefs.ui.menus[menuTitle].refs});
const [ state, setState ] = React.useState(0);
const reactRender = () => {
setState(state + 1);
}
const setRefs = (action: RefsAction, ...args: any) => {
if (action === RefsAction.SET_ITEMS) {
refs.items = [];
if (windowRefs.interacts.selectAnchor) {
refs.items = getNodeContextItems(windowRefs, windowRefs.interacts.selectAnchor);
}
reactRender();
} else if (action === RefsAction.SET_REFS && typeof args[0] === "object") {
for (let objEnt of Object.entries(args[0])) {
refs[objEnt[0]] = objEnt[1];
}
if ("items" in args[0]) {
reactRender();
}
}
}
const itemComps: JSX.Element[] = [];
for (let i = 0; i < refs.items.length; ++i) {
itemComps.push(<MainContextItem windowRefs={windowRefs} setWindowRefs={setWindowRefs} mainContextRefs={refs} itemIdx={i} key={i}/>);
}
return( //doesnt always return the correct jsx elements
<div id={menuTitle} className="contextMenu" ref={refs.ref} style={{visibility: "hidden"}}>
{console.log(itemComps)} // always logs the correct jsx elements
{itemComps}
</div>
);
}
Some important notes:
Anytime the refs.items is updated through the SET_ITEMS path, it sets and renders fine. Anytime it updates through the SET_REFS path is when it sets fine, but doesn't render correctly.
When it doesn't render correctly, it renders the last refs.items array, so setting refs.items through SET_REFS essentially does nothing.

'AuthProvider' cannot be used as a JSX component

I attempt to make auth flow in Context Provider and have troubles with TypeScript.
Here is problem fragment of Provider code:
interface AuthUserProviderProps {
children?: React.ReactNode
}
interface AuthUserInterface {
token: string;
setToken: (value: string) => void;
loggedIn: boolean;
};
setBrand: (value: {}) => void;
}
export const authUser = createContext<AuthUserInterface | null >(null);
const AuthUserProvider = ({ children }: AuthUserProviderProps) => {
if (accessTokenVAR() && !jwtToken?.roles.toString().includes('admin')) {
return isBrowser ? window.location.replace('www.somewhere.com') : ''
}
return (
<authUser.Provider
value={{
token,
setToken,
loggedIn,
}}
>
{ children}
</authUser.Provider>
);
};
export default AuthUserProvider;
App.tsx
export default function App({ Component, pageProps }: AppProps) {
const apolloClient = useApollo(pageProps.initialApolloState);
return (
<ThemeProvider theme={TifTheme}>
<AuthUserProvider> <===This line throws error
<ApolloProvider client={apolloClient} >
<CssBaseline />
<Component {...pageProps} />
</ApolloProvider>
</AuthUserProvider>
</ThemeProvider>
);
}
The error is 'AuthUserProvider' cannot be used as a JSX component. Its return type 'void | "" | Element' is not a valid JSX element. Type 'void' is not assignable to type 'Element.' and it is caused by return redirect.
But I have no clue how to handle this error
Your analysis should be correct, it's the return of the "redirect". You can easily type properties of React functional components with React.FunctionComponent.
I would recommend to write a little useAccess hook and use that to get a boolean for display reasons or redirect the user with a useEffect. As you redirect to a different page, it shouldn't matter what the component returns.
So I modified your code to this, I made some changes and added comments, let me know if it helps.
import { createContext, FunctionComponent, useEffect, useMemo } from "react";
interface AuthUserInterface {
token: string;
setToken: (value: string) => void;
loggedIn: boolean;
setBrand: (value: any) => void;
}
const AuthUser = createContext<AuthUserInterface | null>(null);
const useAccess = (isBrowser: boolean) => {
const hasAccess = useMemo(
() => accessTokenVAR() && !jwtToken?.roles.toString().includes("admin"),
// TODO: check if that's correct, these variables were not in your answer,
// might need to add/pass them to the hook
[accessTokenVAR, jwtToken]
);
useEffect(() => {
// redirect here if has
if (hasAccess && isBrowser) window.location.replace("www.somewhere.com");
}, [isBrowser, hasAccess]);
return hasAccess;
};
interface AuthUserProviderProps {
isBrowser: boolean;
}
const AuthUserProvider: FunctionComponent<AuthUserProviderProps> = ({
children,
isBrowser
}) => {
const hasAccess = useAccess(isBrowser);
return (
<>
{!hasAccess && <p>"No access"</p>}
{hasAccess && (
<AuthUser.Provider
value={{
// TODO: values not in question, where do they come from
token,
setToken,
loggedIn
}}
>
{children}
</AuthUser.Provider>
)}
</>
);
};
export { AuthUserProvider as default, AuthUser };

Resources