i'm learning React Context and i'm using a project that i've build as a test.
After i created my context i'm trying to use it on a component where the following error is showing: Property "processes" does not exist on type "Props | undefined". But it does exist on the interface and it is also on ProcessContext.tsx as well.
interface.tsx
export default interface Props {
children?: any;
processes?: Array<string>;
setProcesses?: (active: any) => void;
updating?: boolean;
setUpdating?: (active: boolean) => void;
getProcesses?: () => void;
}
ProcessContext.tsx
import { createContext, FC, useState } from "react";
import { collection, getDocs } from "firebase/firestore";
import { db } from "../firebase";
import Props from "../Interface/Api";
const ProcessContext = createContext<Props | undefined>(undefined);
export const ProcessProvider: FC<Props> = ({ children }) => {
const [processes, setProcesses] = useState<Array<any>>([]);
const [updating, setUpdating] = useState<boolean>(false);
const getProcesses = async () => {
const processCollectionRef = collection(db, "processes");
const data = await getDocs(processCollectionRef);
setProcesses(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
return (
<ProcessContext.Provider
value={{ processes, setProcesses, updating, setUpdating, getProcesses }}
>
{children}
</ProcessContext.Provider>
);
};
export default ProcessContext;
CourseTable.tsx
import React, { FC, Fragment, useState, useContext, useEffect } from "react";
import CheckIcon from "@mui/icons-material/Check";
import {
Modal,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from "@mui/material";
import ModalData from "./ModalData";
import ProcessContext from "../context/ProcessContext";
import Props from "../Interface/Api";
const CourseTable: FC = () => {
const { processes, updating, getProcesses } = useContext(ProcessContext);
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
useEffect(() => {
getProcesses();
}, [updating]);
}
The issue is that when you create the context, you specify that its type can be undefined
.
createContext()
takes a default value for the context when a component calls it without a provider in its ancestors. You set this value to be undefined
as well.
const ProcessContext = createContext<Props | undefined>(undefined);
This is a totally valid way to initialize a context, it makes sense that the context could be undefined
if there's no provider in the component's ancestors. A component doesn't "know" whether or not there's a provider as an ancestor to provide values for the context it's trying to use.
However, if your context is undefined
, then the expression
const { processes } = useContext(ProcessContext);
results in a javascript error, which is what Typescript is warning you about. This warning will only become a javascript error if you call your context from a component without access to a provider. Still nice that Typescript can catch that before it becomes an error.
There are two ways to fix this:
undefined
when you call useContext()
undefined
For 1. the code could be the following:
const processContextValue = useContext(ProcessContext);
if (processContextValue) {
const { processes, ... } = processContextValue;
// Do stuff with processes
}
That code is just one of the options, there are many ways to handle undefined values. You can check the concept of narrowing
a type (figuring out a type from a union) in the typescript docs.
For 2. a common thing to do is initialize each member in the context object (all items specified in your Props
interface) with a sensible default value (which could also be undefined) instead.
const ProcessContext = createContext<Props>({
children: undefined,
processes: [], // You can always use `undefined`, but you can also use a default value like an empty array
setProcesses: undefined,
updating: false,
setUpdating: undefined, // Functions could be `undefined`
getProcesses: () => null, // or a default function that satisfies the type
});
I also noticed you made every field optional, there's no need to just because you are using Context
, they only have to be optional if undefined
is a possible value.