Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
March 31, 2023 12:11 pm GMT

Advanced ts: playing with generics in a React.Context

This is a story of failure.

I wasn't going to write this down... then I thought, well not all the stories need a happy ending.

To the point.

We have a custom React Context. Don't want to dig deep into its internals. We're ok checking just how we use it:

import { XRouter, XRoute } from '@serious-company.react';...<XRouter>  <XRoute path='/about' component={About} />  <XRoute path='/checkout' component={Checkout} /></XRouter>

&

import { useXRouter } from '@serious-company.react';...const { navigate } = useXRouter();const onClick = () => navigate('/checkout');

... I wonder...

Is it possible to set the exact set of routes?
so both <XRoute /> and navigate() would narrow the type of the path from string to one of my routes?

I was looking for this:

type AvailableRoutes = '/about' | '/checkout';...const onClick = () => navigate('/ceckout');       //  tscconst onClick = () => navigate('/checkout');      // ...<XRouter>  <XRoute path='/abbot' component={About} />      //  tsc  <XRoute path='/about' component={About} />      // </XRouter>

The answer is yep that's possible ... but also No

Generics

Got down to work replacing each declaration of a path, from string to T where <T extends string>:

-type XRouterContextType {-  navigate(path: string) => void;-  history: string[];+type XRouterContextType<T extends string> {+  navigate(path: T) => void;+  history: T[];}
-function XRouter({ children }: XRouterProps) {-  const [history, setHistory] = useState(['/'])--  const navigate: (path: string) => {+function XRouter<T extends string>({ children }): XRouterProps) {+  const [history, setHistory] = useState<T[]>(['/')++  const navigate: (path: T) => {... 
-type XRouteProps {-  path: string;+type XRouteProps<T> {+  path: T;  component: ComponentType;}-function XRoute({ path, component }: XRouteProps) {+function XRoute<T extends string>({ path, component }): XRouteProps<T> {...

And everything was pure and flawless, just like JavaScript...
...till the context object

Creating a React context, we'll export a Context Provider (<XRouter> in these examples) and a way to consume that Context - probably - in a form of useContext() (useXRouter() in these examples)

Thing is, you need to create this context object:

const context = createContext<XRouterContextType>({ ... })

This object has a well defined type. You just can't create an object but keep it with a futuristic Generic T.

A way of understanding Generics in Typescript might be:

"We just don't know T yet.
T will be set by the caller of the function.
They will know.
For now, T is unset"

So, creating an object:

const context = createContext<XRouterContextType>({ ... })

You are the caller. You need to set the type. You can't create an object with a "we'll see" type.

const context = createContext<XRouterContextType<T>>({ ... }) // 

So, is this an impossible problem?

Well, we may implement a work around... exporting a closure function - call it factory, call it Class, call it High Order function, call it whatever you feel - which, when called: creates the context object and returns the Provider component and the hook.

I got a branch with this working:

import { createXRouter } from '@serious-company.react';type AvailableRoutes = '/about' | '/checkout';const { XRouter, XRoute, useXRouter } = createXRouter<AvailableRoutes>();...const { navigate } = useXRouter();const onClick = () => navigate('/ceckout');       //  tscconst onClick = () => navigate('/checkout');      // ...<XRouter>  <XRoute path='/abbot' component={About} />      //  tsc  <XRoute path='/about' component={About} />      // </XRouter>...

But this would mean changing the API of the package

import { XRouter } from '@serious-company.react';=>import { createXRouter } from '@serious-company.react';const { XRouter } = createXRouter<'/a'|'/b'>();

And we finally discarded this work.

As I said, this is a story of failure. At least I did have a good time .

Thanks for reading .


Original Link: https://dev.to/manuartero/advanced-ts-playing-with-generics-in-a-reactcontext-9i

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To