Home > database >  how to call the useContext function?
how to call the useContext function?

Time:03-07

im using the useContext from react , i have add boolean state to context but when i use it in the component it's give me typeErro : the setState is not a function

what im doing wrong this is my code ?

// context file

import { createContext, useContext, Dispatch, SetStateAction } from 'react';

interface TasksContextType {
  isSubmitLoading: boolean;
  setIsSubmitLoading: Dispatch<React.SetStateAction<boolean>>;
}

export const TasksContext = createContext({} as TasksContextType);

export const useTasksContext = () => {
  const context = useContext<TasksContextType>(TasksContext);

  if (!context) {
    throw new Error('useTasksContext should be used within a TasksContext');
  }
  return context;
};

the provider file

//provider file 

import React, { FC, useState } from 'react';

import { TasksContext } from './TasksContext';

export const TasksProvider: FC = ({ children }) => {

  const [isSubmitLoading, setIsSubmitLoading] = useState<boolean>(false);

  return (
    <TasksContext.Provider
      value={{
        isSubmitLoading,
        setIsSubmitLoading,
      }}
    >
      {children}
    </TasksContext.Provider>
  );
};

the component where i use the useContext

//component 

const { setIsSubmitLoading, isSubmitLoading } = useTasksContext();

  useEffect(() => {
    setIsSubmitLoading(mutation.isLoading);
  }, [mutation, setIsSubmitLoading]);

CodePudding user response:

Here's an example based on the code you provided so you can see that it works and how to use it (more explanation below):

TS Playground

<div id="root"></div><script src="https://unpkg.com/[email protected]/umd/react.development.js"></script><script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">

// import ReactDOM from 'react-dom';
// import {
//   default as React,
//   createContext,
//   useContext,
//   useEffect,
//   useState,
//   type Dispatch,
//   type ReactElement,
//   type ReactNode,
//   type SetStateAction,
// } from 'react';

// This Stack Overflow snippet demo uses UMD modules instead of the above import statments
const {
  createContext,
  useContext,
  useEffect,
  useState,
} = React;

type TasksContextType = {
  isSubmitLoading: boolean;
  setIsSubmitLoading: Dispatch<SetStateAction<boolean>>;
};

const TasksContext = createContext(undefined as unknown as TasksContextType);

function useTasksContext (): TasksContextType {
  return useContext<TasksContextType>(TasksContext);
}

function TasksProvider ({children}: { children?: ReactNode }): ReactElement {
  const [isSubmitLoading, setIsSubmitLoading] = useState(false);
  return (
    <TasksContext.Provider
      value={{
        isSubmitLoading,
        setIsSubmitLoading,
      }}
    >{children}</TasksContext.Provider>
  );
}

function App (): ReactElement {
  const {isSubmitLoading, setIsSubmitLoading} = useTasksContext();

  useEffect(() => {
    setTimeout(() => setIsSubmitLoading(true), 1500);
  }, [setIsSubmitLoading]);

  const handleClick = () => setIsSubmitLoading(bool => !bool);

  return (
    <div style={{fontFamily: 'sans-serif'}}>

      <h1>Using the Context API</h1>

      <div>
        <pre>
          <code>
            {JSON.stringify({isSubmitLoading}, null, 2)}
          </code>
        </pre>
      </div>

      <button onClick={handleClick}>Toggle state</button>

    </div>
  );
}

function AppRoot (): ReactElement {
  return (
    <TasksProvider>
      <App />
    </TasksProvider>
  );
}

ReactDOM.render(<AppRoot />, document.getElementById('root'));

</script>

You didn't show in your code where you actually use the ContextProvider and the JSX structure of your app, but it's important that you only use useTasksContext in components that are rendering inside the Provider root. In the example above, I created a component dedicated to rendering the Provider so that it's out of the way and in the main App component, you can just focus on the code.

CodePudding user response:

You are checking if you are within your context provider by checking for a falsy value, but since you initialize context with a value ({}) it never will be undefined:

import { createContext, useContext, Dispatch, SetStateAction } from 'react';

interface TasksContextType {
  isSubmitLoading: boolean;
  setIsSubmitLoading: Dispatch<React.SetStateAction<boolean>>;
}

export const TasksContext = createContext({} as TasksContextType); // Here is your problem, change this to:
// createContext<TasksContextType | undefined>();

export const useTasksContext = () => {
  const context = useContext<TasksContextType>(TasksContext); // You can remove the generic here, it will be inferred 

  if (!context) {
    throw new Error('useTasksContext should be used within a TasksContext');
  }
  return context;
};

Now if you try to use the context somewhere you can't, it will throw an error useTasksContext should be used within a TasksContext Or, if you use it within your context it should work ok.

Also note: You should be memoizing your value prop in TasksContext.Provider, otherwise it will be a new object every time and cause all consumers to rerender.

const value = useMemo(() => ({
        isSubmitLoading,
        setIsSubmitLoading,
      }, [isSubmitLoading]) // you can skip setIsSubmitLoading as it is referentially stable
  • Related