Last active
October 5, 2022 01:31
-
-
Save bmingles/d9a8e7fbe58727128c1b9b82a39a4abb to your computer and use it in GitHub Desktop.
MobX with custom React context hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
/** | |
* Create a React Context that requires a value be provided explicitly (aka. no | |
* default value). Returns the Context Provider + a useContext hook that | |
* encapsulates access to the Context. | |
*/ | |
export function createNamedContext<TValue>(name: string) { | |
const Context = React.createContext<TValue | null>(null) | |
function useContext(): TValue { | |
const value = React.useContext(Context) | |
if (value === null) { | |
throw new Error(`Context value is null for '${name}'.`) | |
} | |
return value | |
} | |
return { | |
Provider: Context.Provider as React.Context<TValue>['Provider'], | |
useContext, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import { observer } from 'mobx-react-lite' | |
const MyComponent: React.FC = () => { | |
const { name, age, loadAge, loadName } = useSomeService() | |
React.useEffect(() => { | |
loadAge() | |
void loadName() | |
}, [loadName]) | |
// Any changes to `name` or `age` in the service will cause React to re-render | |
// this component. Mobx handles the subscription to the `name` and `age` | |
// observables, but you just treat it like it is a string prop | |
return ( | |
<div> | |
{name} {age} | |
</div> | |
) | |
} | |
// This is what makes the component subscribe to mobx observables | |
export default observer(MyComponent) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { makeAutoObservable, runInAction } from 'mobx' | |
import { fetchName } from './api' | |
class SomeService { | |
constructor() { | |
// This makes methods actions and properties observable | |
makeAutoObservable(this) | |
} | |
age = 0 | |
// synchronous action | |
loadAge = () => { | |
this.age = 4 | |
} | |
name = '' | |
// async action | |
loadName = async () => { | |
const name = await fetchName() | |
// This is the one mobx API thing you have to use with async to tell the | |
// framework something is changing. You don't have to use it for change | |
// in syncrhonous methods | |
runInAction(() => { | |
this.name = name | |
}) | |
} | |
} | |
const { Provider, useContext } = createNamedContext<SomeService>('SomeService') | |
export { | |
Provider as SomeServiceContextProvider, | |
useContext as useSomeService | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment