skip to Main Content

I have multiple pages and they all have useState of different types:

//Page 1 ...
const [ShoppingListKey, setShoppingListKey] = useState<keyof ShoppingList>();

//Page 2 ...
const [isTrue, setIsTrue] = useState<boolean>(false);

//Page 3 ... 
const [String, setString] = useState<string>('');

So, I have a component, and what I want it to do, is to take the target state value from its parent, and set its parents’ state to the targeted value:

interface ChildProps {
  targetStateValue: keyof ShoppingList | boolean | string | undefined;
  setStateFunc: Dispatch<React.SetStateAction<keyof ShoppingList | boolean | string | undefined>>
}
export const Child = ({targetStateValue, setStateFunc}: ChildProps) => {
  <button OnClick={()=>{setStateFunc(targetStateValue);}}>BUTTON</button>
}

In ShoppingList Parent:

<Child
  setStateFunc={setShoppingListKey} //ERROR
  targetStateValue={something}
/>

The Error says: Type ‘Dispatch<SetStateAction<keyof ShoppingList | undefined>>’ is not assignable to type ‘Dispatch<SetStateAction<string | boolean | undefined>>’

2

Answers


  1. It is possible that TypeScript has too much inference going on under the hood and and you might just need to simply your types by making a dedicated type for your solution.

    export type TargetStateValue = keyof ShoppingList | boolean | string | undefined
    

    Now you have a dedicated TypeScript type for your targetStateValue to use and you can use it as follows:

    interface ChildProps {
      targetStateValue: TargetStateValue;
      setStateFunc: Dispatch<React.SetStateAction<TargetStateValue>>
    }
    
    Login or Signup to reply.
  2. You can use a generic component.
    Such component would work over a variety of types rather than a single one.

    You define a generic interface for your child props. And a generic component which uses that interface as the type for its props.

    interface ChildProps<T> {
      targetStateValue: T;
      setStateFunc: Dispatch<React.SetStateAction<T>>;
    }
    export const Child = <T>({
      targetStateValue,
      setStateFunc,
    }: ChildProps<T>) => {
      return (
        <button
          onClick={() => {
            setStateFunc(targetStateValue);
          }}
        >
          BUTTON
        </button>
      );
    };
    

    Using the above, we can change T on invocation/definition and based on that the the internal types of the componet will change.

    For simplicity i created a simple interface for ShoppingList and also gave a prefilled value to the state variable ShoppingListKey. The code looks like below:

    interface ShoppingList {
      onion: string;
      tomato: string;
    }
    
    interface ChildProps<T> {
      targetStateValue: T;
      setStateFunc: Dispatch<React.SetStateAction<T>>;
    }
    export const Child = <T>({
      targetStateValue,
      setStateFunc,
    }: ChildProps<T>) => {
      return (
        <button
          onClick={() => {
            setStateFunc(targetStateValue);
          }}
        >
          BUTTON
        </button>
      );
    };
    
    function App() {
      const [ShoppingListKey, setShoppingListKey] =
        useState<keyof ShoppingList>("tomato");
    
      //Page 2 ...
      const [isTrue, setIsTrue] = useState<boolean>(false);
    
      //Page 3 ...
      const [String, setString] = useState<string>("");
      console.log({ ShoppingListKey, isTrue, String });
      return (
        <div className="App">
          <Child<keyof ShoppingList>
            targetStateValue={"onion"}
            setStateFunc={setShoppingListKey}
          />
          <Child<boolean> targetStateValue={true} setStateFunc={setIsTrue} />
          <Child<string> targetStateValue={"test"} setStateFunc={setString} />
        </div>
      );
    }
    
    

    CodeSandbox

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search