Building a Context System in React with TypeScript
Context systems in React provide a way to share data between components without explicitly passing props through every level of the component tree. This challenge asks you to build a simplified context system from scratch, allowing you to understand the underlying mechanisms and gain a deeper appreciation for React's built-in Context API. This is a valuable exercise for solidifying your understanding of React's core concepts and functional programming principles.
Problem Description
You are tasked with creating a basic context system in React using TypeScript. This system should allow components to:
- Provide: A component can "provide" a value to the context.
- Consume: Other components can "consume" the value provided by the context.
The context system should be implemented as a set of functions and React components. You don't need to replicate all the features of React's built-in Context API (e.g., memoization, complex update strategies), but the core functionality of providing and consuming a value should be present.
Key Requirements:
createContext<T>: A function that creates a new context.Trepresents the type of the context value.ProviderComponent: A React component that accepts avalueprop and makes that value available to consuming components.useContextHook: A custom hook that accepts a context object and returns the current context value.
Expected Behavior:
- Components wrapped in a
Providershould receive the value provided by theProvider. - Components not wrapped in a
Providershould receive a default value (you can choose a sensible default, likeundefined). - Changes to the
valueprop of theProvidershould trigger re-renders in consuming components.
Edge Cases to Consider:
- What happens when a component tries to consume a context that is not provided anywhere in its ancestry?
- How will you handle type safety with the generic
T? - Consider how updates to the context value will trigger re-renders.
Examples
Example 1:
Input:
```typescript
const MyContext = createContext<string | null>(null);
function App() {
return (
<MyContext.Provider value="Hello, Context!">
<ComponentA />
</MyContext.Provider>
);
}
function ComponentA() {
const value = useContext(MyContext);
return <div>Value: {value}</div>;
}
Output:
<div>Value: Hello, Context!</div>
Explanation: ComponentA consumes the context value "Hello, Context!" provided by the MyContext.Provider.
Example 2:
Input:
```typescript
const MyContext = createContext<number>(0);
function App() {
return (
<MyContext.Provider value={42}>
<ComponentB />
<ComponentC />
</MyContext.Provider>
);
}
function ComponentB() {
const value = useContext(MyContext);
return <div>Value in B: {value}</div>;
}
function ComponentC() {
const value = useContext(MyContext);
return <div>Value in C: {value}</div>;
}
Output:
<div>Value in B: 42</div>
<div>Value in C: 42</div>
Explanation: Both ComponentB and ComponentC consume the same context value (42) provided by the MyContext.Provider.
Example 3: (Edge Case)
Input:
```typescript
const MyContext = createContext<boolean>(false);
function App() {
return <ComponentD />;
}
function ComponentD() {
const value = useContext(MyContext);
return <div>Value in D: {value}</div>;
}
Output:
<div>Value in D: false</div>
Explanation: ComponentD is not wrapped in a Provider, so it receives the default value (false) defined in createContext.
Constraints
- No external libraries: You must implement the context system using only React and TypeScript. Do not use React's built-in
useContextorcreateContexthooks. - Type Safety: The
createContext<T>function must be generic and enforce type safety for the context value. - Re-renders: Changes to the
valueprop of theProvidershould trigger re-renders in consuming components. While full memoization isn't required, ensure the re-renders are reasonable. - Performance: While not a primary focus, avoid unnecessary re-renders.
Notes
- Think about how you can use React's
useStateor similar mechanisms to track context changes and trigger updates. - Consider how to manage the context object itself and how to associate it with the
ProvideranduseContexthook. - This is a simplified context system. React's built-in Context API has additional features and optimizations that are not required for this challenge. Focus on the core concepts of providing and consuming a value.
- Start by implementing
createContext<T>and theProvidercomponent. Then, implement theuseContexthook. Test each component thoroughly as you build it.