@just-web/react
v9.0.4
Published
Supporting library for using @just-web with React
Downloads
18
Readme
@just-web/react
@just-web/react provides the features needed to use just-web app with React.
Install
# npm
npm install @just-web/react
# yarn
yarn add @just-web/react
# pnpm
pnpm install @just-web/react
#rush
rush add -p @just-web/react
Usage
We will be focusing on how to use just-web app with React here.
If you want to know more about just-web app, you can check out the documentation of @just-web/app.
Following the example from @just-web/app, let's make the sing-along app into a React app.
Let's start with a simple example: a SPA.
To create a React SPA, you need two things from @just-web/react:
<JustAppProvider/>
: the React context provider forJustApp
.useJustAppContext()
: the React context hook to get theJustApp
instance.
import { JustAppProvider } from '@just-web/react'
import ReactDOM from 'react-dom'
import { YourComponent } from './your_component'
import { singAlongApp } from './sing_along_app'
void (async () => {
const app = await singAlongApp.with(reactGizmo).create()
ReactDOM.render(
<JustAppProvider value={app}>
<YourComponent />
</JustAppProvider>,
document.getElementById('root')
)
})()
// your_component.tsx
import { useJustAppContext } from '@just-web/react'
import type { SingAlongApp } from './sing_along_app'
export function YourComponent() {
const app = useJustAppContext<SingAlongApp>()
return (
<div>
<MusicPlayer start={() => app.miku.sing()} />
</div>
)
}
For Micro Frontends (MFEs), just JustAppProvider
and useJustAppContext()
may not be sufficient.
Especially if you have components from different MFEs intertwined together.
In that case, you will also need:
createJustAppContext()
: to create a React context for each MFE.reactGizmo
: to register the specific React context for each MFE.
In the example below, we have two MFEs: miku
and rin
.
Each of them is a MFE with its own JustApp
instance.
They need to add reactGizmo
, and register their own React context.
They are doing it differently to show two different ways of doing it.
const MikuAppContext = createJustAppContext<MikuApp>()
const mikuApp = await singAlongApp.with(reactGizmo)
.create(app => app.react.providers.register(({ children }) => (
<MikuAppContext.Provider value={app}>
{children}
</MikuAppContext.Provider>
)))
const RinAppContext = createJustAppContext<RinApp>()
const rinAppIncubator = singAlongApp.with(reactGizmo)
.init(app => app.react.providers.register(({ children }) => (
<RinAppContext.Provider value={app}>
{children}
</RinAppContext.Provider>
)))
const rinApp = await rinAppIncubator.create()
Then, in the React app,
you can use the JustAppProvider
for both mikuApp
and rinApp
,
and they will work together.
ReactDOM.render(
<JustAppProvider value={mikuApp}>
<MikuSinging />
<JustAppProvider value={rinApp}>
<MikuDancing />
<RinSinging />
</JustAppProvider>
</JustAppProvider>,
document.getElementById('root')
)
Inside your components,
you can still use useJustAppContext()
with the JustAppContext
you have created to get your specific app.
import { useJustAppContext } from '@just-web/react'
import { MikuAppContext } from './miku_app'
export function MikuSinging() {
const app = useJustAppContext(MikuAppContext)
return (
<div>
<MusicPlayer start={() => app.miku.sing()} />
</div>
)
}
Alternatively, you can use <MikuAppContext.Provider>
or <RinAppContext.Provider>
too,
but typically there is no need to do so.
In fact, you can keep them off your package exports.
The host app only need the mikuApp
, rinApp
, and the JustAppProvider
and everything will work.
useStore
useStore() Allows you to use store
from @just-web/states
.
It is similar to useState()
in React,
but work on a store instead of a local state.
import { createStore } from '@just-web/states'
import { useStore } from '@just-web/react'
const store = createStore({ counter: 0 })
const Component = () => {
const [counter, setCounter] = useStore(
store,
// get which part of the store to use as state
s => s.counter,
// updateStore when state changes
s=>{ s.counter = counter }
)
return <div>{counter}</div>
}
useStoreContext
useStore() is useful to consume an app-level store:
// from another file
import { store } from './store'
// or create at the module scope
const store = createStore(...)
const Component = () => {
const [] = useStore(store, ...)
}
But it doesn't work well if we want to have a local store that live and die with the DOM.
In React, that's when you can use createContext()
and useContext()
.
Here, we can use createStoreContext() and useStoreContext() to achieve the same thing with Store.
And the usage is much simpler and efficient thanks to the underlying immer implementation.
import { createStoreContext, useStoreContext } from '@just-web/react'
import { createStore } from '@just-web/states'
type YourStore = { counter: number }
const YourContext = createStoreContext<YourStore>() // no default value
const YourProvider = (props) => {
const store = createStore<YourStore>({ counter: 0 })
return <YourContext.Provider value={store} {...props}/>
}
const YourConsumer = () => {
const [counter, setCounter] = useStoreContext(
YourContext,
s => s.counter,
(s, v) => { s.counter = v }
)
return (
<>
<div>counter: {counter}</div>
<button onClick={() => setCounter(counter + 1)}>Increment</button>
</>
)
}
reactGizmo
As seen in the example above,
reactGizmo
provides a react.providers
where you can register React context providers.
You can use it to add other providers to the app.
For example, i18n, theme, etc.
import { createApp } from '@just-web/app'
import reactPlugin, { AppContextProvider } from '@just-web/react'
function YourApp() {
const app = createApp({...}).extned(reactPlugin)
app.react.providers.register(({ children }) => <YourProvider {...}>{children}</YourProvider>)
return (
<JustAppProvider value={app}>
...
</JustAppProvider>
)
}
The providers will be added to the app in the order they are registered,
inside the <JustAppProvider>
.
This allows any gizmo to add their needed providers to the app, without having the app to know about them.