Simple library enabling to render preact
components from a registry + config and some utilities around JSON-RPC as a hook.
Overall goal is to configure its UI.
Important
|
this library is not yet deployed on npm so you will have to install it locally yourself for now. |
import { render } from 'preact';
import { Dynamic } from '@yupiik/dynamic';
const registry = {
component1: Component1,
component2: Component2,
component3: Component3,
}
render(
<Dynamic
registry={registry}
options={{
name: 'component1',
options: {
// whatever options component1 supports
}
}}
/>,
document.getElementById('app'));
FromConfiguratonHoc
is a companion for Dynamic
(a HoC actually on top of Dynamic
) which is purely configuration oriented.
It handles callbacks, children etc from plain JSON.
Configuration schema (properties of the component):
{
registry = undefined,
parentState = undefined,
parentDispatch = undefined,
options: {
name,
options: {
useReducerCallback: {
initialState = undefined,
reducerJsonLogic = false,
} = {},
useEffectCallback = [],
callbacks = {},
configuration = {},
} = {},
},
}
Property | Description |
---|---|
registry |
component registry (key matching the component name) |
name |
the component name in the registry |
useEffectCallback |
the useEffect callbacks properties definition in JSON-Logic format, it can be an array or an object (behaves as an array of 1 element). Each element is a JSON-Logic expression. |
useReducerCallback |
the useReducer callbacks. initialState is the state initial value and reducerJsonLogic the JSON-Logic implementation which takes the current state and action as parameters. JSONLogic take the state and dispatch functions as parameters. |
callbacks |
the callbacks properties definition in JSON-Logic format, key is the callback name and value its JSON-Logic chain. The JSON-Logic can use variables |
configuration |
properties to passthrough the underlying component. |
parentState |
if no initialState is passed it will be used to overwrite it, enables to forward through children the state when children are compatible. |
parentDispatch |
parent dispatch callback (to be able to modify parent state). |
In most applications, you will mix Dynamic
with custom components.
For that case it is worth injecting the registry as a context at a high level of the application:
import { render } from 'preact';
import { Dynamic, ComponentRegistryContext } from '@yupiik/preact';
import MyComponent from './my-component';
const registry = {
component1: Component1,
component2: Component2,
component3: Component3,
}
render(
<ComponentRegistryContext.Provider value={registry}>
<MyComponent/>
</ComponentRegistryContext.Provider>,
document.getElementById('app'));
This simple snippet enables any nested component like MyComponent
to reuse Dynamic
without specifying the registry which allows to write reusable component more easily:
export const MyComponent = () => (
<Dynamic
options={{
name: 'component1',
options: {
configuration: {
// whatever options component1 supports
},
wrapperProps: {
// whatever must be bound on the HoC which will wrap component1
// (can be useful with preact-router like components for ex which require the next immediate child to have specific props)
},
},
}}
/>
);
In a real application, you will often have a tree of Dynamic
.
If your backend supports bulking (JSON-RPC is highly recommended but GraphQL or other protocols can support it), we recommend you to wrap Dynamic
to enable to bulk the requests by stashing them and to issue a single HTTP request for the component tree.
It is mainly a matter of abstracting the communication with the backend for the components and having a toggle to know if all subcomponents got an initial render or not.
@yupiik/antd-registry
provides a component registry which works with Dynamic
or FromConfiguratonHoc
.
react-bootstrap
can be integrated importing it (import * as reactBootstrap
) and converting the import to a registry using @yupiik/dynamic
nestedRegistry
fonction.
Similarly react-feather
which does not use nested components can be converted to a registry the same way but using simpleRegistry
function is more efficient instead of nestedRegistry
.
Using this method on react-bootstrap
, you will get in in the registry, as of today, the following list of component:
-
Accordion
-
Accordion.Button
-
Accordion.Collapse
-
Accordion.Item
-
Accordion.Header
-
Accordion.Body
-
AccordionButton
-
AccordionCollapse
-
AccordionContext
-
AccordionContext.Consumer
-
AccordionContext.Provider
-
AccordionHeader
-
AccordionItem
-
Alert
-
Alert.Link
-
Alert.Heading
-
AlertHeading
-
AlertLink
-
Anchor
-
Badge
-
Breadcrumb
-
Breadcrumb.Item
-
BreadcrumbItem
-
Button
-
ButtonGroup
-
ButtonToolbar
-
Card
-
Card.Img
-
Card.Title
-
Card.Subtitle
-
Card.Body
-
Card.Link
-
Card.Text
-
Card.Header
-
Card.Footer
-
Card.ImgOverlay
-
CardBody
-
CardFooter
-
CardGroup
-
CardHeader
-
CardImg
-
CardImgOverlay
-
CardLink
-
CardSubtitle
-
CardText
-
CardTitle
-
Carousel
-
Carousel.Caption
-
Carousel.Item
-
CarouselCaption
-
CarouselItem
-
CloseButton
-
Col
-
Collapse
-
Container
-
Dropdown
-
Dropdown.Toggle
-
Dropdown.Menu
-
Dropdown.Item
-
Dropdown.ItemText
-
Dropdown.Divider
-
Dropdown.Header
-
DropdownButton
-
DropdownDivider
-
DropdownHeader
-
DropdownItem
-
DropdownItemText
-
DropdownMenu
-
DropdownToggle
-
Fade
-
Figure
-
Figure.Image
-
Figure.Caption
-
FigureCaption
-
FigureImage
-
FloatingLabel
-
Form
-
Form.Group
-
Form.Control
-
Form.Floating
-
Form.Check
-
Form.Switch
-
Form.Label
-
Form.Text
-
Form.Range
-
Form.Select
-
Form.FloatingLabel
-
FormCheck
-
FormCheck.Input
-
FormCheck.Label
-
FormControl
-
FormControl.Feedback
-
FormFloating
-
FormGroup
-
FormLabel
-
FormSelect
-
FormText
-
Image
-
InputGroup
-
InputGroup.Text
-
InputGroup.Radio
-
InputGroup.Checkbox
-
ListGroup
-
ListGroup.Item
-
ListGroupItem
-
Modal
-
Modal.Body
-
Modal.Header
-
Modal.Title
-
Modal.Footer
-
Modal.Dialog
-
ModalBody
-
ModalDialog
-
ModalFooter
-
ModalHeader
-
ModalTitle
-
Nav
-
Nav.Item
-
Nav.Link
-
NavDropdown
-
NavDropdown.Item
-
NavDropdown.ItemText
-
NavDropdown.Divider
-
NavDropdown.Header
-
NavItem
-
NavLink
-
Navbar
-
Navbar.Brand
-
Navbar.Collapse
-
Navbar.Offcanvas
-
Navbar.Text
-
Navbar.Toggle
-
NavbarBrand
-
NavbarCollapse
-
NavbarOffcanvas
-
NavbarText
-
NavbarToggle
-
Offcanvas
-
Offcanvas.Body
-
Offcanvas.Header
-
Offcanvas.Title
-
OffcanvasBody
-
OffcanvasHeader
-
OffcanvasTitle
-
OffcanvasToggling
-
Overlay
-
OverlayTrigger
-
PageItem
-
Pagination
-
Pagination.First
-
Pagination.Prev
-
Pagination.Ellipsis
-
Pagination.Item
-
Pagination.Next
-
Pagination.Last
-
Placeholder
-
Placeholder.Button
-
PlaceholderButton
-
Popover
-
Popover.Header
-
Popover.Body
-
PopoverBody
-
PopoverHeader
-
ProgressBar
-
Ratio
-
Row
-
SSRProvider
-
Spinner
-
SplitButton
-
Stack
-
Tab
-
Tab.Container
-
Tab.Content
-
Tab.Pane
-
TabContainer
-
TabContent
-
TabPane
-
Table
-
Tabs
-
ThemeProvider
-
Toast
-
Toast.Body
-
Toast.Header
-
ToastBody
-
ToastContainer
-
ToastHeader
-
ToggleButton
-
ToggleButtonGroup
-
ToggleButtonGroup.Button
-
Tooltip
import { render } from 'preact';
import { useJsonRpc } from '@yupiik/use-json-rpc';
export const MyComponent = ({}) => {
const [ loading, error, data ] = useJsonRpc({
payload: {
jsonrpc: '2.0',
method: 'my-server-method',
params: {},
},
// optional
endpoint: '/jsonrpc',
needsSecurity: true,
fetchOptions: {},
dependencies: [],
});
if (loading) {
return (<Skeleton />);
}
if (error) {
return (<ErrorAlert error={error} />);
}
if (!data) {
return (<div>No data.</div>);
}
return (<pre>{JSON.stringify(data, null, 2)}</pre>);
};
useJsonRpc
hook takes the following properties in its object parameter:
Name | Default | Description |
---|---|---|
payload |
- |
JSON-RPC request, can be an array or an object. |
endpoint |
/jsonrpc |
JSON-RPC endpoint to call |
needsSecurity |
true |
Should |
fetchOptions |
|
Any |
dependencies |
|
|
fetch |
|
The |
providedData |
- |
The JSON-RPC data result (if provided, ie thruthy, it will be used and the server call will be bypassed). |
When you keep needsSecurity
to true
, you must pass a SecurityContext.Provider
:
import { SecurityContext } from '@yupiik/use-json-rpc';
export const MyComponent = () => (
<SecurityContext.Provider value={{access_token: ....}}>
<MyComponentUsingUseJsonRpc />
</SecurityContext.Provider>
);
Tip
|
it is often done at a high level of the application to be shared accross all components. |