5

I'm trying to create a generic hooks to handle the button input elements, which return array of input value, bind object and reset handler.

Component

import React, { useState } from "react";
import { useInput } from "../services/FormInputHooks";

export type AddTransactionProps = {};

export const AddTransaction: React.FC<AddTransactionProps> = () => {
    const [text, bindText, resetText] = useInput<string>("");
    const [amount, bindAmount, resetAmount] = useInput<number>(0.0);

    return (
        <>
            <h3>Add new transaction</h3>
            <form>
                <div className="form-control">
                    <label htmlFor="text">Text</label>
                    <input
                        type="text"
                        {...bindText}
                        placeholder="Enter text.."
                    />
                </div>
                <div className="form-control">
                    <label htmlFor="amount">
                        Amount <br />
                        (negative - expense, positive - income)
                    </label>
                    <input
                        type="number"
                        {...bindAmount}
                        placeholder="Enter amount.."
                    />
                </div>
                <button className="btn"> Add Transaction</button>
            </form>
        </>
    );
};

export default AddTransaction;

Hook

import { useState } from "react";

export function useInput<T>(
    initialValue: T
): [T, any, React.Dispatch<React.SetStateAction<T>>] {
    const [value, setValue] = useState<T>(initialValue);

    const reset = () => {
        setValue(initialValue);
    };

    const bind = {
        value,
        onChange: e => {
            setValue(e.target.value);
        }
    };

    return [value, bind, reset];
}

Problem I'm Facing

Parameter 'e' implicitly has an 'any' type.  TS7006

    12 |     const bind = {
    13 |         value,
  > 14 |         onChange: e => {
       |                   ^
    15 |             setValue(e.target.value);
    16 |         }
    17 |     };

Though i've specified the type of any for the bind object, it shows the above error. I've even tried with following code to specify the return type.

[T, {T: onChange: (e: any) => void}, React.Dispatch<React.SetStateAction<T>>]

2 Answers 2

7

The problem is not the type you define for the hooks return value, it is that the bind object does not have any type annotation so its onChange method's e param will be implicitly any.

One possible sulution to fix its type annotation:

import { useState, ChangeEventHandler } from "react";

interface ResetFunction {
    (): void
}

interface Bind<T> {
  value: T,
  onChange: ChangeEventHandler<any>
}

export function useInput<T>(
    initialValue: T
): [T, Bind<T>, ResetFunction] {
    const [value, setValue] = useState<T>(initialValue);

    const reset = () => {
        setValue(initialValue);
    };

    const bind: Bind<T> = {
        value,
        onChange: e => {
            setValue(e.target.value);
        }
    };

    return [value, bind, reset];
}

Typescipt playground

Sign up to request clarification or add additional context in comments.

4 Comments

I think React.Dispatch<React.SetStateAction<T>> would be better for ResetFunction. It's more implicit?? Can you please correct me??
If you passed the setValue from the hook directly, the React.Dispatch<React.SetStateAction<T>> would be a correct type, but you don't, you create a new function (reset) which takes no parameter and returns nothing, and it calls your setValue function, thus the extra type for ResetFunction in my example.
So React.Dispatch specifies the return type to be callable and React.SetStateAction<T> for that specific callable to set state. Am i right?? Please share any related docs if you have any. Thanks !!
I just worked my way through React's type definition. Dispatch type: type Dispatch<A> = (value: A) => void;: defines a generic function, which takes a value and returns void. SetStateAction: type SetStateAction<S> = S | ((prevState: S) => S); a generic type, whether the generic value itself or a function which gets a parameter and returns the generic type. So Dispatch<SetStateAction<S> will be basically a function which gets a single value or a function (wich gets an S and returns an S) and returns void.
2
import React, { useState } from "react";

export function useInput<T>(
    initialValue: T
): [T, any, React.Dispatch<React.SetStateAction<T>>] {
    const [value, setValue] = useState<T>(initialValue);

    const reset = () => {
        setValue(initialValue);
    };

    const bind = {
        value,
        onChange: (e: React.ChangeEvent<any>) => {
            setValue(e.target?.value);
        }
    };

    return [value, bind, reset];
}

Playground

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.