You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
You can also use the [Class.contextType](https://reactjs.org/docs/context.html#classcontexttype) or [Context.Consumer](https://reactjs.org/docs/context.html#contextconsumer) API, let us know if you have trouble with that.
1547
-
1548
-
_[Thanks to @AlvSovereign](https://github.com/typescript-cheatsheets/react/issues/97)_
1535
+
Call `useContext` to read and subscribe to the context.
1549
1536
1550
-
#### Extended Example
1537
+
```tsx
1538
+
import { useContext } from"react";
1551
1539
1552
-
Using `createContext` with an empty object as default value.
1540
+
const MyComponent = () => {
1541
+
const theme =useContext(ThemeContext);
1553
1542
1554
-
```tsx
1555
-
interfaceContextState {
1556
-
// set the type of state you want to handle with context e.g.
1557
-
name:string|null;
1558
-
}
1559
-
// set an empty object as default state
1560
-
const Context =createContext({} asContextState);
1561
-
// set up context provider as you normally would in JavaScript [React Context API](https://reactjs.org/docs/context.html#api)
1543
+
return <p>The current theme is {theme}.</p>;
1544
+
};
1562
1545
```
1563
1546
1564
-
Using `createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/) to make a `createCtx` with **no `defaultValue`, yet no need to check for `undefined`**:
1547
+
#### Without default context value
1565
1548
1566
-
```ts
1567
-
import { createContext, useContext } from"react";
1549
+
If you don't have any meaningful default value, specify `null`:
Notice the explicit type arguments which we need because we don't have a default `string` value:
1575
+
Now that the type of the context can be `null`, you'll notice that you'll get a `'currentUser' is possibly 'null'` TypeScript error if you try to access the `username` property. You can use optional chaining to access `username`:
along with the non-null assertion to tell TypeScript that `currentUser`is definitely going to be there:
1587
+
However, it would be preferrable to not have to check for `null`, since we know that the context won't be `null`. One way to do that is to provide a custom hook to use the context, where an error is thrown if the context is not provided:
([Playground here](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQduEAdqvLgK5SXMwCqqLFADCLGFgAe8ALyYqMAHS5KycaN6SYAHjZRgzAOYA+ABQdmAEywF9WCwEIAlPQLn8wFnACivABYdUYDQYYFwAcUosEMMTRzgAbzo4OCZWdi4efkEoOFlsPEUArHVxKRNObixeASESzWckuEoYLmY4LQtgADcjAAkvABkBgHkEisyaqAUYCD4wMFq0LFiAX3stAHpOnvoVuldmd08AQXnYhMbm1vbxqqzasU0FAAViLuArHK7kABsOLGkZAAyr5kAB3ZhkIyNZJaHwwfyBYKhCJYKL6AxwDbQ2EbW7VbJ1KQvN4fIRGXZAA)) This is a quick and easy fix, but this loses type-safety, and if you forget to supply a value to the Provider, you will get an error.
1610
-
1611
-
2. We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**:
1612
-
1613
-
```tsx
1614
-
import { createContext, useContext } from"react";
1615
-
1616
-
/**
1617
-
* A helper to create a Context and Provider with no upfront default value, and
1618
-
* without having to check for undefined all the time.
1619
-
*/
1620
-
function createCtx<Aextends {} |null>() {
1621
-
const ctx =createContext<A|undefined>(undefined);
1622
-
function useCtx() {
1623
-
const c =useContext(ctx);
1624
-
if (c===undefined)
1625
-
thrownewError("useCtx must be inside a Provider with a value");
1626
-
returnc;
1627
-
}
1628
-
return [useCtx, ctx.Provider] asconst; // 'as const' makes TypeScript infer a tuple
1629
-
}
1630
-
1631
-
// Usage:
1632
-
1633
-
// We still have to specify a type, but no default!
[View in the TypeScript Playground](https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MOAaE+uAA4pQsJ84oDliCweIl5PfsIcTHKll1XWd5wWJU1XlOBOk0YB9GmAAJckABkUIAeSWXBfxXf9KlEZMwEEKA5DQLAFmGbtOkYOCEPoRN6kURpmg4IiP1VV91RgxdgL-IR1wFOBRV8bYyEDKJTEUMhphRTor0sW972AJ8XzfeJGBkt5qJ4idcP4-ljWmeigA)
1651
-
1652
-
3. You can go even further and combine this idea using `createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/).
1653
-
1654
-
```tsx
1655
-
import { createContext, useContext } from"react";
1656
-
1657
-
/**
1658
-
* A helper to create a Context and Provider with no upfront default value, and
1659
-
* without having to check for undefined all the time.
1660
-
*/
1661
-
function createCtx<Aextends {} |null>() {
1662
-
const ctx =createContext<A|undefined>(undefined);
1663
-
function useCtx() {
1664
-
const c =useContext(ctx);
1665
-
if (c===undefined)
1666
-
thrownewError("useCtx must be inside a Provider with a value");
1667
-
returnc;
1668
-
}
1669
-
return [useCtx, ctx.Provider] asconst; // 'as const' makes TypeScript infer a tuple
1670
-
}
1671
-
1672
-
// usage
1673
-
1674
-
exportconst [useCtx, SettingProvider] =createCtx<string>(); // specify type, but no need to specify value upfront!
1675
-
exportfunction App() {
1676
-
const key =useCustomHook("key"); // get a value from a hook, must be in a component
1677
-
return (
1678
-
<SettingProvidervalue={key}>
1679
-
<Component />
1680
-
</SettingProvider>
1681
-
);
1682
-
}
1683
-
exportfunction Component() {
1684
-
const key =useCtx(); // can still use without null check!
1685
-
return <div>{key}</div>;
1686
-
}
1687
-
```
1688
-
1689
-
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA)
1690
-
1691
-
4. Using `createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters:
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA)
1741
-
1742
-
5. A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful.
Using a runtime type check in this will has the benefit of printing a clear error message in the console when a provider is not wrapping the components properly. Now it's possible to access `currentUser.username` without checking for `null`:
1749
1612
1750
1613
```tsx
1751
-
interfaceProviderState {
1752
-
themeColor:string;
1753
-
}
1614
+
import { useContext } from"react";
1754
1615
1755
-
interfaceUpdateStateArg {
1756
-
key:keyofProviderState;
1757
-
value:string;
1758
-
}
1616
+
const MyComponent = () => {
1617
+
const currentUser =useCurrentUser();
1759
1618
1760
-
interfaceProviderStore {
1761
-
state:ProviderState;
1762
-
update: (arg:UpdateStateArg) =>void;
1763
-
}
1619
+
return <p>Username: {currentUser.username}.</p>;
1620
+
};
1621
+
```
1764
1622
1765
-
const Context =createContext({} asProviderStore); // type assertion on empty object
1623
+
##### Type assertion as an alternative
1766
1624
1767
-
classProviderextendsReact.Component<
1768
-
{ children?:ReactNode },
1769
-
ProviderState
1770
-
> {
1771
-
publicreadonly state = {
1772
-
themeColor: "red",
1773
-
};
1625
+
Another way to avoid having to check for `null` is to use type assertion to tell TypeScript you know the context is not `null`:
1774
1626
1775
-
private update = ({ key, value }:UpdateStateArg) => {
0 commit comments