Skip to content

Commit 5ae084d

Browse files
Update Context (#956)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent 46b33c9 commit 5ae084d

File tree

4 files changed

+166
-167
lines changed

4 files changed

+166
-167
lines changed

src/routes/concepts/context.mdx

Lines changed: 69 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,28 @@ This offers a way to access state across an application without passing props th
2121
Context is created using the [`createContext`](/reference/component-apis/create-context) function.
2222
This function has a `Provider` property that wraps the component tree you want to provide context to.
2323

24+
<TabsCodeBlocks>
25+
<div id="/context/create.js">
2426
```jsx
2527
import { createContext } from "solid-js";
2628

2729
const MyContext = createContext();
30+
```
31+
</div>
32+
<div id="/context/component.jsx">
33+
```jsx
34+
import { MyContext } from "./create";
2835

29-
export const Provider = (props) => {
30-
return <MyContext.Provider>{props.children}</MyContext.Provider>;
36+
export function Provider (props) {
37+
return (
38+
<MyContext.Provider>
39+
{props.children}
40+
</MyContext.Provider>
41+
)
3142
};
3243
```
44+
</div>
45+
</TabsCodeBlocks>
3346

3447
## Providing context to children
3548

@@ -38,106 +51,41 @@ Once a value is passed to the `Provider`, it is available to all components that
3851

3952
When passing a single value, it can be directly passed to the `value` prop:
4053

41-
```jsx
54+
```jsx title="/context/component.jsx"
4255
import { createContext, useContext } from "solid-js";
43-
44-
const MyContext = createContext("initial");
56+
import { MyContext } from "./create";
4557

4658
const Provider = (props) => (
4759
<MyContext.Provider value="new value">{props.children}</MyContext.Provider>
4860
);
4961
```
5062

51-
However, if you need to access multiple values, they must be passed as an object literal, or using double curly braces (`{{ }}`):
52-
53-
```jsx {15-19}
54-
import { createContext, createSignal } from "solid-js";
55-
56-
const MyContext = createContext("default value");
57-
58-
export const Provider = (props) => {
59-
const stringVal = "new value";
60-
const numberVal = 3;
61-
const objVal = {
62-
foo: "bar",
63-
obj: true,
64-
};
65-
66-
return (
67-
<MyContext.Provider
68-
value={{
69-
stringVal,
70-
numberVal,
71-
objVal,
72-
}}
73-
>
74-
{props.children}
75-
</MyContext.Provider>
76-
);
77-
};
78-
```
63+
<Callout type="tip" title="Complex Types">
64+
When passing multiple values (as an `array` or `object`), it is recommended to use a [store](/reference/component-apis/create-context#usage).
65+
</Callout>
7966

8067
## Consuming context
8168

8269
Once the values are available to all the components in the context's component tree, they can be accessed using the [`useContext`](/reference/component-apis/use-context) utility.
8370
This utility takes in the context object and returns the value(s) passed to the `Provider`:
8471

85-
```jsx {10}
72+
```jsx title="/context/component.jsx"
8673
import { createContext, useContext } from "solid-js";
87-
88-
const MyContext = createContext();
74+
import { MyContext } from "./create";
8975

9076
const Provider = (props) => (
91-
<MyContext.Provider value="new value">{props.children}</MyContext.Provider>
77+
<MyContext.Provider value="new value">
78+
{props.children}
79+
</MyContext.Provider>
9280
);
9381

9482
const Child = () => {
9583
const value = useContext(MyContext);
96-
return <>{value}</>;
97-
};
98-
99-
export const App = () => (
100-
<Provider>
101-
<Child /> {/* "new value" */}
102-
</Provider>
103-
);
104-
```
10584

106-
When you are passing multiple values into the `Provider`, you can destructure the context object to access the values you need.
107-
This provides a readable way to access the values you need without having to access the entire context object:
108-
109-
```jsx {26}
110-
import { createContext, useContext, createSignal } from "solid-js";
111-
112-
const MyContext = createContext();
113-
114-
const Provider = (props) => {
115-
const stringVal = "new value";
116-
const numberVal = 3;
117-
const objVal = {
118-
foo: "bar",
119-
obj: true,
120-
};
121-
return (
122-
<MyContext.Provider
123-
value={{
124-
stringVal,
125-
numberVal,
126-
objVal,
127-
}}
128-
>
129-
{props.children}
130-
</MyContext.Provider>
131-
);
132-
};
133-
134-
const Child = () => {
135-
const { stringVal, numberVal } = useContext(MyContext);
13685
return (
137-
<>
138-
<h1>{stringVal}</h1>
139-
<span>{numberVal}</span>
140-
</>
86+
<span>
87+
{value}
88+
</span>
14189
);
14290
};
14391

@@ -148,7 +96,7 @@ export const App = () => (
14896
);
14997
```
15098

151-
## Customizing context utilities
99+
## Customizing Context Utilities
152100

153101
When an application contains multiple context objects, it can be difficult to keep track of which context object is being used.
154102
To solve this issue, you can create a custom utilities to create a more readable way to access the context values.
@@ -158,8 +106,7 @@ This also provides you with the option of re-using the `Provider` component in o
158106

159107
```jsx
160108
import { createSignal, createContext, useContext } from "solid-js";
161-
162-
const CounterContext = createContext();
109+
import { CounterContext } from "~/context/counter";
163110

164111
export function CounterProvider(props) {
165112
let count = 0;
@@ -211,7 +158,7 @@ export function CounterProvider(props) {
211158
}
212159
```
213160

214-
## Updating context values
161+
## Updating Context Values
215162

216163
[Signals](/concepts/signals) offer a way to synchronize and manage data shared across your components using context.
217164
You can pass a signal directly to the `value` prop of the `Provider` component, and any changes to the signal will be reflected in all components that consume the context.
@@ -234,12 +181,10 @@ export function App() {
234181
</div>
235182
<div id="Context.jsx">
236183
```jsx
237-
import { createSignal, createContext, useContext } from "solid-js";
238-
239-
const CounterContext = createContext(); // create context
184+
import { createSignal, useContext } from "solid-js";
240185

241186
export function CounterProvider(props) {
242-
const [count, setCount] = createSignal(props.count || 0);
187+
const [count, setCount] = createSignal(props.initialCount || 0);
243188
const counter = [
244189
count,
245190
{
@@ -263,7 +208,7 @@ export function useCounter() { return useContext(CounterContext); }
263208
```
264209
</div>
265210
<div id="Child.jsx">
266-
```jsx
211+
```tsx title="/context/counter-component.tsx"
267212
import { useCounter } from "./Context";
268213

269214
export function Child(props) {
@@ -290,21 +235,50 @@ When working with TypeScript, this can introduce type issues that make it diffic
290235

291236
To solve this issue, a default value can be specified when creating a context object, or errors can be handled manually through the use of a custom `useMyContext` utility:
292237

293-
```jsx
294-
import { createContext, useContext } from "solid-js";
295-
296-
const MyContext = createContext<string>();
238+
```tsx title="/context/counter-component.tsx"
239+
import { useContext } from "solid-js";
297240

298241
function useMyContext() {
299242
const value = useContext(MyContext);
300-
if (value === undefined) {
301-
throw new Error("useMyContext must be used within a MyContext.Provider");
243+
244+
if (!value) {
245+
throw new Error("Missing context Provider");
302246
}
247+
303248
return value;
304249
}
305250

306251
function Child() {
307252
const value = useMyContext();
253+
308254
return <div>{value}</div>;
309255
}
310256
```
257+
258+
## Common issues with `createContext` and `useContext`
259+
260+
If no default value is passed to `createContext`, it is possible for `useContext` to return `undefined`.
261+
262+
<Callout type="info" title="More on default values">
263+
Read more about default values in the [`createContext`](/reference/component-apis/create-context) entry.
264+
</Callout>
265+
266+
Because of this, if an initial value was not passed to `createContext`, the TS type signature of `useContext` will indicate that
267+
the value returned might be `undefined` (as mentioned above).
268+
This can be quite annoying when you want to use the context inside a component, and particularly when immediately destructuring the context.
269+
Additionally, if you use `useContext` and it returns `undefined` (which is often, but not always, the result of a bug), the error message thrown at runtime can be confusing.
270+
271+
The most common solution for it is to wrap all uses of `useContext` in a function that will explicitly throw a helpful error if the context is `undefined`.
272+
This also serves to narrow the type returned, so TS doesn't complain.
273+
As an example:
274+
275+
```ts title="/context/counter-component.tsx"
276+
function useCounterContext() {
277+
const context = useContext(CounterContext)
278+
if (!context) {
279+
throw new Error("can't find CounterContext")
280+
}
281+
return context
282+
}
283+
```
284+

0 commit comments

Comments
 (0)