18

I was following this video ("JWTUser Sessions with ReactJS & GraphQL...") when at this time the guy destructures useParams() method from react-router-dom library.

In my case, that didn't work since I am getting this error:

This is the whole code at this point:

import React, { useState, useContext } from 'react';
import { useParams, useHistory } from 'react-router-dom';
import { useConfirmMutation } from '../gql/generated/graphql';
import { AppStateContext } from './provider';

export const Confirm: React.FC = () => {
    const history = useHistory();
    const { appSetAuthToken, appClearAuthToken, gqlError } = useContext(AppStateContext);

    const [show, setShow] = useState(false);
    const [email, setEmail] = useState('');
    const [confirm] = useConfirmMutation();
    const { token } = useParams();

    const handleFormSubmit = async (e: React.FormEvent) => {
        e.preventDefault();

        try {
            setShow(false);
            appSetAuthToken(token);
            const { data } = await confirm({ variables: email });
        } catch {

        }
    };

    if (token === undefined || token === '')
        return <div>Enlace de confirmación de usuario inválido</div>;

    return (
        <div>
            <div>Página de confirmación de usuario</div>
            {show ? <div>{gqlError.msg}</div> : undefined}
            <form>
                <div>
                    <input
                        value={email}
                        placeholder='Correo electrónico'
                        type='email'
                        onChange={e => { setEmail(e.target.value); }}
                    />
                </div>
                <button type='submit'>Confirmar</button>
            </form>
        </div>
    );
};

I have also tried the same on CodeSandbox but it works. Not sure, where is my mistake. Can you see that mistake?

Maramal
  • 3,145
  • 9
  • 46
  • 90
  • 4
    Check this reply on a similar question https://stackoverflow.com/a/60080307/2206971 – mbkfa93 Oct 24 '20 at 21:10
  • I haven't found that question before. Since I was following the video I didn't expect that. Thanks. – Maramal Oct 24 '20 at 21:23
  • Does this answer your question? [required url param on React router v5 with typescript, can be undefined](https://stackoverflow.com/questions/59085911/required-url-param-on-react-router-v5-with-typescript-can-be-undefined) – Joel Bourbonnais Nov 25 '20 at 13:01

2 Answers2

67

useParams is generic. You need to tell typescript which params you are using by specifying the value of the generic like this: useParams<MyParams>(); In your case it is:

const { token } = useParams<{token?: string}>();

Which says that token is either a string or undefined.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
  • Just curious, is that exactly same as? `const { token } = useParams<{token: string | undefined}>();` – apena May 11 '21 at 21:09
  • @apena technically yes, practically don't do it – revelt Sep 04 '21 at 18:47
  • @revelt thanks for your comment. I had the same issue. Could you elaborate on the preferred use of the "?" symbol as opposed to "| undefined" ? – CodeCody Oct 05 '21 at 00:49
  • 1
    @CodeCody `| undefined` means that the property must be present but the value could be `undefined`. `?` makes the property optional. In this particular case where you are destructuring that object they will both have the same impact, which is that `token` will have the type `string | undefined` (The react-router types won’t raise any errors if the params aren’t actually the type that you say they are). In other situations there is a difference because `{}` is not assignable to `{token: string | undefined}` but it is assignable to `{token?: string}`. `{token: undefined}` is assignable to both. – Linda Paiste Oct 05 '21 at 16:25
  • 1
    @LindaPaiste it's not working – Azhar Uddin Sheikh Sep 22 '22 at 13:40
-5

Yep, typescript can't destructure generic plain objects like {}. But any works like a charm.

const { token } = useParams() as any
Airon Tark
  • 8,900
  • 4
  • 23
  • 19
  • 6
    This is not OK. One does not simply use typescript to use `any` where it should not be used. It has its place, definetly not here. – Croolman Jul 16 '21 at 11:17
  • @Croolman Just to be a little more explicit: You should only use `any` when an object truly can be anything. In this case, we know what properties the object returned from `useParams()` will have, which means logically we know it isn't `any` object. Just as a rule of thumb: if you know what properties an object will have, you shouldn't be using `any`. If you know anything at all about what properties an object will have, it should not be of type `any`. Using `any` like this defeats the entire purpose of using typescript. – ICW Jan 03 '22 at 19:42