Home > Blockchain >  Trouble enforcing a function's parameter interface when using React.useContext()
Trouble enforcing a function's parameter interface when using React.useContext()

Time:01-06

I have only been using TypeScript a couple months, and I just noticed that the compiler is not enforcing the shape of data a function accepts if that function is accessed through React.useContext().

This setup here is not exactly what I have going on, but it more or less shows the problem I am trying to figure out.

import * as React from 'react';

//==>> Reducer State Interface
interface InitialStateInterface {
    handleSettingFooBar: Function;
    foo: string | null;
    bar: boolean;
}

//==>> Function Interface
interface PayloadInterface {
    foo?: string | null;
    bar?: boolean;
}

//==> Reducer
interface ReducerInterface {
    type: string;
    payload: PayloadInterface;
}

enum ActionTypes {
    SET_FOO = 'SET_FOO',
    SET_BAR = 'SET_BAR',
}

const initialState: InitialStateInterface = {
    handleSettingFooBar: () => null,
    foo: null,
    bar: false,
};

const SomeContext = React.createContext<InitialStateInterface>(initialState);

const ContextReducer = (state: any, { type, payload }: ReducerInterface) => {
    switch (type) {
        case ActionTypes.SET_FOO:
            return { ...state, foo: payload.foo };
        case ActionTypes.SET_BAR:
            return { ...state, bar: payload.bar };
        default:
            return state;
    }
};

const SomeProvider = () => {
    const [state, dispatch] = React.useReducer(ContextReducer, initialState);

    function handleSettingFooBar(data: PayloadInterface) {
        let { foo, bar } = data;
        if (foo) dispatch({ type: ActionTypes.SET_FOO, payload: { foo } });
        if (bar) dispatch({ type: ActionTypes.SET_BAR, payload: { bar } });
    }

        /** Okay, of course, no error */
    handleSettingFooBar({ foo: 'test' }); 

        /** Error as expected, type does not match */
    handleSettingFooBar({ foo: false }); 

        /** Error as expected, key does not exist */
    handleSettingFooBar({ randomKey: 'cant do this' }); 

    return <SomeContext.Provider value={{ ...state, handleSettingFooBar }} />;
};


/* ===>  But when writing a component that uses that context <=== */

export const SomeComponent = () => {
    const { handleSettingFooBar } = React.useContext(SomeContext);

        /** Why is the compiler not yelling at me here??? */
    handleSettingFooBar({ randomKey: 'hahaha' }); 
};

export { SomeProvider, SomeContext };

I have tried putting the interface in when calling the context, like this:

const { handleSettingFooBar } = React.useContext<InitialStateInterface>(SomeContext);

But that made no difference.

I am expecting that if somebody is authoring a component that uses this context and its provided functions, that it will regulate the data (at compile time, of course) they try to pass in so a generic setter may not add a value that does not belong in the context reducer state.

Please help, thanks!

CodePudding user response:

The SomeContext has the InitialStateInterface type which defines handleSettingFooBar as handleSettingFooBar: Function, and it does not know how you actually implemented it.

You can change that to handleSettingFooBar: (data:PayloadInterface) => void and then the typescript would know what kind of input should be allowed for it.

  • Related