简体   繁体   中英

Why do i get the error setLoading doesnt exist on type {} while using createContext using react and typescript?

i want to use the context provider to set the state of loading using react and typescript.

what i am trying to do? i want to set loading state to true or false when request starts and ends and provide access to this loading state to all components within main component.

i have tried to do so like in code below,

function useAnother(Id: string) {
    const [compId, setCompId] = React.useState(undefined);
    const [isLoading, setIsLoading] = React.useState(false);
    const comp = useCurrentComp(Id);
    const load = useLoad();
    if (comp && comp.id !== compId) {
        setCompId(comp.id);
        const prevCompId = compId !== undefined;
        if (prevCompId) {
            setIsLoading(true);
            load().then(() => {
                setIsLoading(false);
            });
        }
    }
}

function Main ({user}: Props) {
    useAnother(user.id); //fetching isLoading here from useHook
    return (
        <Wrapper>
            <React.suspense>
                <Switch>
                    <Route 
                        path="/" 
                        render={routeProps => (
                            <FirstComp {...routeProps} />
                        )}
                    />
                    <Route 
                        path="/items" 
                        render={routeProps => (
                            <SecondComp {...routeProps} />
                        )}
                    />
                    //many other routes like these
                </Switch>
            </React.suspense>
        </Wrapper>
    );
}


function FirstComponent (isLoading) {
    return (
        <Wrapper isLoading={isLoadin}/>
    );
}

with the context provider i have tried like below,

function useAnother(Id: string) {
    const [compId, setCompId] = React.useState(undefined);
    const loading = React.useContext(LoadingContext); //accessing context here
    const comp = useCurrentComp(Id);
    const load = useLoad();
    if (comp && comp.id !== compId) {
        setCompId(comp.id);
        const prevCompId = compId !== undefined;
        if (prevCompId) {
            loading.setIsLoading(true); //gives error setIsLoading doesnt exist on type {}
            load().then(() => {
                loading.setIsLoading(false); //gives error setIsLoading doesnt exist on type {}
            });
        }
    }
}



export const LoadingContext = React.createContext({});

export const LoadingContextProvider = ({ children }: any) => {
    const [isLoading, setIsLoading] = React.useState(false);

    return (
        <LoadingContext.Provider
            value={{
                isLoading,
                setIsLoading,
            }}
        >
            {children}
        </LoadingContext.Provider>
    );
};


function Main ({user}: Props) {
    useAnother(user.id); //fetching isLoading here from useHook
    return (
        <Wrapper>
            <React.suspense>
                <LoadingContext.Provider>
                    <Switch>
                        <Route 
                            path="/" 
                            render={routeProps => (
                                <FirstComp {...routeProps} />
                            )}
                        />
                        <Route 
                            path="/items" 
                            render={routeProps => (
                                <SecondComp {...routeProps} />
                            )}
                        />
                        //many other routes like these
                   </Switch>
                </LoadingContext.Provider>
            </React.suspense>
        </Wrapper>
    );
}

I am not sure why i get the error setIsLoading doesnt exist on type {} when i try to access the context in useAnother hook.

could someone help me with this. thanks.

If you initialize context like export const LoadingContext = React.createContext({}); , TS will infer the context state value as an empty object. You have to be explicit about types when creating the context.

For example,

interface ILoadingCtxState {
  isLoading: boolean;
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
  // this is type for setIsLoading if you directly pass the setter you get from the useState hook
}

const initialLoadingState: ILoadingCtxState = {
  isLoading: false,
  setIsLoading: () => {}
}

export const LoadingContext = React.createContext<ILoadingCtxState>(initialLoadingState);

In the provider component,

export const LoadingContextProvider: React.FC = ({ children }) => {
  const [isLoading, setIsLoading] = React.useState<boolean>(false);

  // you can memoize the value that you pass by using React.useMemo
  // example,
  const memoized = React.useMemo(() => ({isLoading, setIsLoading}), [isLoading])

    return (
        <LoadingContext.Provider
            value={memoized}
        >
            {children}
        </LoadingContext.Provider>
    );
};

When you use React.useContext to consume to LoadingContext you have to destructure the values, example

const {isLoading, setIsLoading} = React.useContext(LoadingContext)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM