When I make a component that relies on something like the currently chosen language for example, or maybe a special query function to fetch data, and I provide either of these things via a ContextProvider
and I make use of them with useContext()
, does this not create a hidden dependency? Essentially a dependency on a sort of global-ish variable?
When defining properties on the function of a functional component, if I am missing arguments/properties when writing my TSX/typescript I will get a warning, because the function's contract/interface/signature is defined and can be statically analyzed and checked.
So, am I wrong to say that this can't be done at all with contexts? If someone were to reuse the components and the documentation did not announce the dependency it could be an issue, no? Can one use the context API and still have some sort of static dependency checking, or must one use component props / functional args to ensure dependencies are met?
If the context API does hide dependencies, why do people use it?
Addendum:
I thought all of this was relatively clear, but I will clarify that ultimately I am asking whether when importing and using a component that depends on context, if it is somehow apparent in any IDE what context it requires, without looking into its internal code or reading documentation, before runtime.
Yes, you are right, React context dependencies can't be checked at compile time.
If somebody uses your component (which uses useContext
) without an appropriate provider higher in the tree, it will fail at runtime (not compile time).
The context is stored in the component tree managed by React, which is created dynamically at runtime.
Runtime dependencies aren't unique to React:
Think of a function that loads e.g. a config.ini
file.
The compiler can not check if that file will exist when the program is run by a user.
Or if a function relies on e.g. a document.getElementById("my-container")
,
somebody could try to use that function without also adding a e.g. <div id="my-container">
.
The function must handle such cases gracefully.
A common way to inform a developer who uses your component about this dependency is to wrap it into a separate custom hook (which is a good idea anyway), and log or throw an error, like this:
export const useMySharedState = function(): TMySharedState {
const context = useContext( mySharedState );
if( context === undefined ){
throw new Error(
'useMySharedState must be used within a MySharedStateProvider'
);
}
return context || initialState;
};
You would use React context at a point where you wouldn't find an answer to the question "what else could I use" ?
So if you are fine with what you have without React context, then stay with it. But localization is a common use case for using React context.
Some reasons to use React context
are described on
passing-data-deeply-with-context.
You might be interested in the section before-you-use-context
and following.)
One situation where you can hardly avoid using React context is when you need some state tied to the currently rendered DOM tree, and not e.g. a global variable. This becomes especially critical when server side rendering is involved, e.g. you don't want a server side global variable that is shared between all users.