All Projects → t83714 → fractal-component

t83714 / fractal-component

Licence: MIT license
A javascript library that can help you to encapsulate decoupled UI component easily

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to fractal-component

isomorphic-react-redux-saga-ssr
Isomorphic, React, Redux, Saga, Server Side rendering, Hot Module Reloading, Ducks, Code Splitting
Stars: ✭ 19 (+46.15%)
Mutual labels:  redux-saga, server-side-rendering
enlite-starter
Enlite Starter - React Dashboard Starter Template with Firebase Auth
Stars: ✭ 28 (+115.38%)
Mutual labels:  redux-saga, jss
Js Stack From Scratch
🛠️⚡ Step-by-step tutorial to build a modern JavaScript stack.
Stars: ✭ 18,814 (+144623.08%)
Mutual labels:  server-side-rendering, jss
Hapi React Hot Loader Example
Simple React Hot Loading example with Hapi Server-side rendering
Stars: ✭ 44 (+238.46%)
Mutual labels:  redux-saga, server-side-rendering
Ssr Sample
A minimum sample of Server-Side-Rendering, Single-Page-Application and Progressive Web App
Stars: ✭ 285 (+2092.31%)
Mutual labels:  redux-saga, server-side-rendering
next-saga-example
Example of using next.js with redux saga
Stars: ✭ 24 (+84.62%)
Mutual labels:  redux-saga, server-side-rendering
Js Stack Boilerplate
Final boilerplate code of the JavaScript Stack from Scratch tutorial –
Stars: ✭ 145 (+1015.38%)
Mutual labels:  server-side-rendering, jss
movies
Real world isomorphic application for movies search, based on Webpack 5 / Express / React 17 + Redux-Saga / Bootstrap 4.6 + CSS Modules / i18next / SSR
Stars: ✭ 20 (+53.85%)
Mutual labels:  redux-saga, server-side-rendering
Typescript Hapi React Hot Loader Example
Simple TypeScript React Hot Loading example with Hapi Server-side rendering
Stars: ✭ 44 (+238.46%)
Mutual labels:  redux-saga, server-side-rendering
Starter Lapis
Cutting edge starter kit
Stars: ✭ 72 (+453.85%)
Mutual labels:  redux-saga, server-side-rendering
react-pdfs
Generate PDFs with React
Stars: ✭ 32 (+146.15%)
Mutual labels:  server-side-rendering
numvalidate
Phone number validation REST API
Stars: ✭ 54 (+315.38%)
Mutual labels:  server-side-rendering
react-redux-hackernews
React Redux Hackernews Application
Stars: ✭ 19 (+46.15%)
Mutual labels:  redux-saga
prism
(No longer in development). Experimental compiler for building isomorphic web applications with web components.
Stars: ✭ 106 (+715.38%)
Mutual labels:  server-side-rendering
core
Server side rendering with The Elm Architecture in Deno
Stars: ✭ 16 (+23.08%)
Mutual labels:  server-side-rendering
docsify-ssr-demo
NOTICE: This repo is outdated and SSR is work-in-progress.
Stars: ✭ 61 (+369.23%)
Mutual labels:  server-side-rendering
handwritten-digit-recognition-tensorflowjs
In-Browser Digit recognition with Tensorflow.js and React using Mnist dataset
Stars: ✭ 40 (+207.69%)
Mutual labels:  redux-saga
FlexDotnetCMS
A powerful, flexible, decoupled and easy to use and Fully Featured ASP .NET CMS, it can also be used as a Headless CMS
Stars: ✭ 45 (+246.15%)
Mutual labels:  server-side-rendering
react-hooks-course
Code for TylerMcGinnis.com's "React Hooks" course
Stars: ✭ 37 (+184.62%)
Mutual labels:  reacthooks
vue-jss-plugin
JSS plugin implementation for Vue.js
Stars: ✭ 24 (+84.62%)
Mutual labels:  jss

fractal-component

GitHub stars npm version unpkg npm bundle size (minified + gzip) Build Status

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 (See the live demo on CodePen). 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 / unmount
  • Hot Plug Saga & Auto mount / unmount
  • Namespaced Redux Store
  • Auto Component State Management & Redux Store Mapping
  • Enhanced Server Side Rendering (SSR) Support
  • Support React Function Components via React Hooks API (See Example App)

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:

Typical Container Container Component Structure Diagram

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 <script> tag of an HTML page.

Documents

Table of Contents

Changelog

** New: Added React New Context API support (Requires React Version 16.6.0 and above)

** New: Support Function Component via New Hooks API (Requires React Version 16.8.0 and above)

** New: Allows to share state data among components via SharedState

Please find the complete Changelog from here.

FAQ

  • Why I can't call setState method anymore / Why hook API doesn't offer setState?

fractal-component manages your component state (either Function or Class Component) in a global Redux Store. You will need to dispatch an Action to trigger state changes in Reducers (either your component reducer or global reducer). Once the state in the global redux store is updated, fractal-component will update the React component state automatically in order to trigger re-rendering.

  • What's Namespace Data?

When create re-usable component, we should keep in mind that the component could possible be reused multiple times on the same screen. There may be some initialization operation that should only perform once when the first component instance of the reuable component is rendered on screen and, consequently,some clean-up job that should be only perform once after the last component instance of the component is unmounted.

React, currently, doesn't offer life cycle hooks for this purspose. fractal-component provides namespaceInitCallback & namespaceInitCallback for this purpose.

Any return value from the namespaceInitCallback will be stored as Namespace Data and can be retrieved any time before all component instances of the reuable component are unmounted. More info can be found from ComponentManager / getNamespaceData()

  • Why we don't need to use React Lifecycle Methods anymore?

It's recommended to put any code produces side effects into the Component Namespaced Saga. It covers most use cases of React Lifecycle Methods as shown below:

import { cancelled } from "redux-saga/effects"
function*(effects) {
    try {
        /**
         * Safe to perform any side effects, monitor any actions 
         * or setup subscription here.
         * You can also fork a new Saga to handle side effects / error concurrently
         * to avoid letting main saga exit.
        */
    } catch (e) {
        // --- optional handle any errors here
    } finally {
        /**
         * When your component is unmonted, your saga will be cancelled.
         * Code will reach the finally block and you can test 
         * whether the saga is cancelled by `yield cancelled()` effects
        */
        if (yield cancelled()) {
            // --- logic that should execute only on cancellation
        } else {
            // --- logic that should execute in all situations
        }
    }
};

This will work well even for server-side rendering. Saga will be started when ReactDOMServer try to render your components into string and you can call AppContainer.destroy() (after save a store snapshot) to clean up all resources and cancel all sagas.

  • Why use symbol as action type?

fractal-component requires all action type is a symbol to avoid action type collision between different components. However, a symbol generally cannot be serialise directly via JSON.stringify. This may cause some troubles if you want to store actions and reply later to implment time travel or undo feature.

To overcome this issue, fractal-component provides an ActionRegistry facility to serialise an Action. More details can be found from AppContainer / serialiseAction()

Quick Start

You don't have to use Webpack / Babel to compile / bundle any code before you can play with components built with fractal-component. Simply create a HTML file with the content below, open it with your web browser and you are good to go. The single HTML file will pull the UMD version of published components from CDN and run in your browser. Components included by this demo are:

Bundled version of the complete exampleApp can be found from here. Or here if you prefer React Function Components.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>ExampleApp Demo</title>
        <!--
        Load `babel-standalone` to support JSX in script tag
    -->
        <script src="https://unpkg.com/@babel/standalone@^7.0.0/babel.min.js"></script>
        <script src="https://unpkg.com/react@~16.8.0/umd/react.production.min.js"></script>
        <script src="https://unpkg.com/prop-types@~15.6.2/prop-types.min.js"></script>
        <script src="https://unpkg.com/react-dom@~16.8.0/umd/react-dom.production.min.js"></script>
        <script src="https://unpkg.com/redux-saga@~1.0.0/dist/redux-saga.umd.min.js"></script>
        <script src="https://unpkg.com/fractal-component@latest/dist/fractal-component.min.umd.js"></script>
        <script src="https://unpkg.com/[email protected]/dist/jss.min.js"></script>
        <script src="https://unpkg.com/[email protected]/dist/jss-preset-default.min.js"></script>
        <script src="https://unpkg.com/[email protected]/lodash.min.js"></script>
        <script src="https://unpkg.com/@fractal-components/random-gif@latest/dist/@fractal-components/random-gif.min.umd.js"></script>
        <script src="https://unpkg.com/@fractal-components/random-gif-pair@latest/dist/@fractal-components/random-gif-pair.umd.js"></script>
        <script src="https://unpkg.com/@fractal-components/random-gif-pair-pair@latest/dist/@fractal-components/random-gif-pair-pair.min.umd.js"></script>
        <script src="https://unpkg.com/@fractal-components/counter@latest/dist/@fractal-components/counter.min.umd.js"></script>
        <script src="https://unpkg.com/@fractal-components/toggle-button@latest/dist/@fractal-components/toggle-button.min.umd.js"></script>
    </head>

    <body>
        <div id="app_root"></div>
        <script type="text/babel">
            const appContainer = new FractalComponent.AppContainer({
                reduxDevToolsDevOnly: false
            });

            const styles = {
                table: {
                    display: "flex",
                    "flex-wrap": "wrap",
                    margin: "0.2em 0.2em 0.2em 0.2em",
                    padding: 0,
                    "flex-direction": "rows"
                },
                cell: {
                    "box-sizing": "border-box",
                    "flex-grow": 0,
                    overflow: "hidden",
                    padding: "0.2em 0.2em",
                    "border-bottom": "none",
                    display: "flex",
                    "align-items": "center",
                    "justify-content": "flex-start"
                }
            };

            const createStyleSheet = _.once(() => {
                return jss
                    .create()
                    .createStyleSheet(styles, {
                        generateClassName: FractalComponent.utils.createClassNameGenerator(
                            "exampleApp"
                        )
                    })
                    .attach();
            });

            function App() {
                const { classes } = createStyleSheet();
                const ActionForwarder = FractalComponent.ActionForwarder;
                return (
                    <div>
                        <div className={classes.table}>
                            <div className={classes.cell}>
                                {/*
                            RandomGif / RandomGifPair / RandomGifPairPair support apiKey property as well
                            You can supply your giphy API key as component property
                        */}
                                <RandomGif.default namespacePrefix="exampleApp/RandomGif" />
                                {/*Forward `NEW_GIF` actions (and convert to `INCREASE_COUNT`) to ToggleButton for processing*/}
                                <ActionForwarder
                                    namespacePrefix="exampleApp/RandomGif"
                                    pattern={RandomGif.actionTypes.NEW_GIF}
                                    relativeDispatchPath="../ToggleButton/*"
                                    transformer={
                                        Counter.actionTypes.INCREASE_COUNT
                                    }
                                />
                            </div>
                            <div className={classes.cell}>
                                <Counter.default namespacePrefix="exampleApp/Counter" />
                            </div>
                        </div>
                        <div className={classes.table}>
                            <div className={classes.cell}>
                                <RandomGifPair.default namespacePrefix="exampleApp/RandomGifPair" />
                                {/*Forward `NEW_GIF` actions (and convert to `INCREASE_COUNT`) to ToggleButton for processing*/}
                                <ActionForwarder
                                    namespacePrefix="exampleApp/RandomGifPair"
                                    pattern={RandomGif.actionTypes.NEW_GIF}
                                    relativeDispatchPath="../ToggleButton/*"
                                    transformer={
                                        Counter.actionTypes.INCREASE_COUNT
                                    }
                                />
                            </div>
                            <div className={classes.cell}>
                                {/*
                            ToggleButton acts as a proxy --- depends on its status
                            add an `toggleButtonActive`= true / false field to all actions
                            and then forward actions to Counter
                        */}
                                <ToggleButton.default
                                    namespacePrefix="exampleApp/ToggleButton"
                                    pattern={Counter.actionTypes.INCREASE_COUNT}
                                    relativeDispatchPath="../Counter/*"
                                    transformer={
                                        Counter.actionTypes.INCREASE_COUNT
                                    }
                                />
                            </div>
                        </div>
                        <div>
                            <RandomGifPairPair.default namespacePrefix="exampleApp/RandomGifPairPair" />
                            {/*Forward `NEW_GIF` actions (and convert to `INCREASE_COUNT`) to ToggleButton for processing*/}
                            <ActionForwarder
                                namespacePrefix="exampleApp/RandomGifPairPair"
                                pattern={RandomGif.actionTypes.NEW_GIF}
                                relativeDispatchPath="../ToggleButton/*"
                                transformer={Counter.actionTypes.INCREASE_COUNT}
                            />
                        </div>
                    </div>
                );
            }

            ReactDOM.render(
                <FractalComponent.AppContainerContext.Provider
                    value={appContainer}
                >
                    <App />
                </FractalComponent.AppContainerContext.Provider>,
                document.getElementById("app_root")
            );
        </script>
    </body>
</html>
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].