@bloc-arch/react
Package provides hooks and components for BLoC architecture in React.
Table of Contents
Related packages
Setup and Usage Example (TypeScript)
// src/index.tsx
// ...
import { BlocProvider } from './blocs/setup-blocs'
ReactDOM.render(
<React.StrictMode>
<BlocProvider> {/* <= */}
<App />
</BlocProvider>
</React.StrictMode>,
document.getElementById('root')
)
// src/blocs/setup-blocs.ts
// ...
import { setupBlocs } from '@bloc-arch/react'
export const { BlocProvider, useWeakBloc, useBloc } = setupBlocs({
sample: (dependency: any) => new SampleBloc(dependency),
// ...
})
// src/blocs/sample/SampleBloc.ts
// (generated by @bloc-arch/cli)
import { Bloc } from '@bloc-arch/core'
import * as SampleEvents from './SampleEvent'
import * as SampleStates from './SampleState'
export class SampleBloc extends Bloc<
SampleEvents.SampleEvent,
SampleStates.SampleState
> {
constructor() {
super(new SampleStates.Initial())
}
async *mapEventToState(event: SampleEvents.SampleEvent) {
// if (event instanceof SampleEvents.Foo) {
yield new SampleStates.Loading()
await new Promise((resolve) => setTimeout(resolve, 2000)) // wait 2s
yield new SampleStates.NotInitial()
/*
} else if (event instanceof SampleEvents.Bar) {
...
}
*/
}
}
// src/App.tsx
// ...
import { BlocBuilder, useBlocRenderProp } from '@bloc-arch/react'
import { useBloc } from './blocs/setup-blocs'
function App() {
const sampleBloc = useBloc('sample')('dependency')
return (
<>
<button
onClick={() => {
sampleBloc.dispatch(new SampleEvents.Foo())
}}
>
click
</button>
<BlocBuilder
bloc={sampleBloc}
renderer={useBlocRenderProp((state) => {
if (state instanceof SampleStates.Loading) {
return <div>loading state</div>
} else if (state instanceof SampleStates.NotInitial) {
return <div>not initial state</div>
}
return <div>initial state</div>
}, [])}
/>
</>
)
}
Docs
setupBloc
Takes as an argument object with functions creating blocs and returns BlocProvider, useBloc and useWeakBloc.
// setup blocs:
const { BlocProvider, useBloc, useWeakBloc } = setupBlocs({
sample: () => new SampleBloc()
})
useBloc
Returns a function to instantiate bloc. Bloc is created only if it doesn't exist. Disposes bloc on component unmount.
// setup blocs:
export const { BlocProvider, useBloc } = setupBlocs({
//...
sample: () => new SampleBloc(),
})
// component:
const bloc = useBloc("sample")()
useWeakBloc
Returns a function to get a specified bloc if is available. Bloc is available between the time of creation and disposal.
// component:
const getSampleBloc = useWeakBloc("sample")
// ...
const handleClick = React.useCallback(() => {
getSampleBloc()?.dispatch(new SampleEvents.Foo())
}, [getSampleBloc])
BlocProvider
Provider of blocs. Use property blocFactories to provide mocked blocks in case of testing.
// root component:
<BlocProvider blocFactories={{ sample: (dependency: any) => new SampleBlocMock() }}>
...
</BlocProvider>
BlocBuilder, useBlocRenderProp
BlocBuilder subscribes to bloc and maps states to views by the use of property renderer. Property listener is optional. Use it to react on state changes.
"useBlocRenderProp" is a pretty much a React.useCallback with ability to deduce type of state (TypeScript).
// component:
<BlocBuilder
bloc={sampleBloc}
listener={React.useCallback((state: SampleStates.SampleState) => {
if (state instanceof SampleStates.NotInitial) {
props.callback()
}
}, [props.callback])}
renderer={useBlocRenderProp((state) => {
if (state instanceof SampleStates.Loading) {
return <div>loading state</div>
} else if (state instanceof SampleStates.NotInitial) {
return <div>not initial state</div>
} else {
return <div>{props.someProp}</div>
}
}, [props.someProp])}
/>