fractal-component

fractal-component
is a javascript library that can help you to encapsulate decoupled UI component easily. It aims to provide a one-stop solution that allows state store (redux) management, actions (messages, events) processing & routing, side-effect management and component styling to be encapsulated into one single software module. You can then reuse your component to create new components (composition), use in a different project or publish it as a NPM module. You can not only use those components in web browsers but also can render them at server-side (SSR) & create redux store snapshot easily (see example).
In order to achieve that, fractal-component
introduce the following features to react / redux ecosystem:
Multicast
Actions- Namespaced Actions
- Serializable Symbol Action Type
Hot Plug
Redux Reducer & Auto mount / unmountHot Plug
Saga & Auto mount / unmount- Namespaced Redux Store
- Auto Component State Management & Redux Store Mapping
- Enhanced Server Side Rendering (SSR) Support
With fractal-component
, you can create reusable Container Components and construct scalable fractal architecture application while still enjoy the convenience of Redux dev tool & predictable single global store.
A typical structure of Container Components created by fractal-component
is illustrated in the graph below:

To try it out, take a look at the example apps and find out how fractal-component
solves the classical Scalable Architecture Problem.
Install
yarn add fractal-component
or
npm install --save fractal-component
Alternatively, you may use the UMD builds from unpkg directly in the
Documents
Table of Contents
Quick Start
A Reusable RandomGif Component. You can also find complete source code here.
import React from "react";
import PropTypes from "prop-types";
import { AppContainerUtils } from "fractal-component";
import reducer from "./reducers";
import saga from "./sagas";
import * as actions from "./actions";
import * as actionTypes from "./actions/types";
import partialRight from "lodash/partialRight";
import jss from "jss";
import styles from "./styles";
class RandomGif extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
imageUrl: null,
error: null
};
this.componentManager = AppContainerUtils.registerComponent(this, {
namespace: "io.github.t83714/RandomGif",
reducer: reducer,
saga: partialRight(saga, props.apiKey),
actionTypes,
allowedIncomingMulticastActionTypes: [actionTypes.REQUEST_NEW_GIF],
namespaceInitCallback: componentManager => {
const styleSheet = jss
.createStyleSheet(styles, {
generateClassName: componentManager.createClassNameGenerator()
})
.attach();
return { styleSheet };
},
namespaceDestroyCallback: ({ styleSheet }) => {
styleSheet.detach();
}
});
}
render() {
const { styleSheet } = this.componentManager.getNamespaceData();
const { classes } = styleSheet;
return (
<div className={classes.table}>
<div className={classes.cell}>RandomGif</div>
<div
className={`${classes.cell} ${classes["image-container"]}`}
>
{this.state.imageUrl &&
!this.state.isLoading &&
!this.state.error && (
<img
alt="Gif"
src={this.state.imageUrl}
className={`${classes.image}`}
/>
)}
{(!this.state.imageUrl || this.state.isLoading) &&
!this.state.error && (
<p>
{this.state.isLoading
? "Requesting API..."
: "No GIF loaded yet!"}
</p>
)}
{this.state.error && (
<p>{`Failed to request API: ${this.state.error}`}</p>
)}
</div>
{this.props.showButton && (
<div className={`${classes.cell} `}>
<button
onClick={() => {
this.componentManager.dispatch(
actions.requestNewGif()
);
}}
disabled={this.state.isLoading}
>
{this.state.isLoading
? "Requesting API..."
: "Get Gif"}
</button>
</div>
)}
</div>
);
}
}
RandomGif.propTypes = {
showButton: PropTypes.bool,
apiKey: PropTypes.string
};
RandomGif.defaultProps = {
showButton: true,
apiKey: "xxxxxxxxxxxxxxx"
};
export default RandomGif;
const exposedActionTypes = {
NEW_GIF : actionTypes.NEW_GIF,
LOADING_START: actionTypes.LOADING_START,
LOADING_COMPLETE: actionTypes.LOADING_COMPLETE
};
const exposedActions = {
requestNewGif: actions.requestNewGif
};
export { exposedActionTypes as actionTypes, exposedActions as actions };