Home > Software design >  How to destructure useContext hook in Typescript with null default value?
How to destructure useContext hook in Typescript with null default value?

Time:08-03

I created context with null initial value that I am now accessing in other component. However when I destructure it to access the variables form the context then I get ts error:

Property 'users' does not exist on type 'GithubContextProps

function UserResults() {
  const appContext = useContext(GithubContext)

  const { users, loading, fetchUsers } = appContext
  

  useEffect(() => {
    fetchUsers()
  }, [fetchUsers])

  if (!loading) {
    return (
      <div className='grid grid-cols-1 gap-8 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2'>
        {users.map((user : User) => (
          <UserItem key={user.id} user={user} />
        ))}
      </div>
    )
  } else {
    return <Spinner />
  }
}

And here is where i created context:

export type User = {
  id: number;
  login: string;
  avatar_url: string;
}

interface GithubContextProps {
  users: User[];
  loading: boolean;
  fetchUsers: () => void;
}


const GithubContext = createContext<GithubContextProps | null>(null)

I tried solving this with using conditional checking whether appContext is not null but then useEffect will throw errors that it cannot be used conditionally in this component. Any idea how to solve it without resorting to turning off React strict mode?

CodePudding user response:

How about:

const { users, loading, fetchUsers } = appContext ?? {};

You'll get undefined when appContext is null.

CodePudding user response:

try with

 const GithubContext = createContext<GithubContextProps | null>({{ users:[], loading:false, fetchUsers:null }});

CodePudding user response:

useEffect, and every other hook must always be called every time the component re-renders, so conditionally calling useEffect will not work. I would try placing the check inside the useEffect then place another check for our jsx:

useEffect(() => {
  if (appContext) appContext.fetchUsers()
}, [fetchUsers])

if (!appContext || appContext.loading) return <Spinner />;
// you can then destructure
const { users, loading, fetchUsers } = appContext;
// ...

Or a more better solution, you can pass the context value as props to <UserResults /> instead so that you'll only need to check on the parent component, which is arguably more cleaner

const Parent = () => {
  const appContext = useContext(GithubContext);
  if (!appContext) return <Spinner />;
  return <UserResults appContext={appContext} />;
}

const UserResults = ({ appContext }) => {
  const { users, loading, fetchUsers } = appContext;
  // ...
}
  • Related