Skip to content

Latest commit

 

History

History
361 lines (262 loc) · 12.4 KB

README.md

File metadata and controls

361 lines (262 loc) · 12.4 KB

bricks

Tools for connecting high-level Redux components (“Bricks”).

Installation

npm install --save @modular-toolkit/bricks

Note: by default, the npm package exposes ES5-compatible code (transpiled through Babel).

If you want to use the untranspiled code (highly recommended), us the esnext version, which is included in the same npm package (more info here).

API

The Brick Manager allows you to “wire up” Bricks in your application by running each Brick's root saga with the Redux Saga middleware, preparing each Brick's selectors to work with the global Redux state, and replacing the current root Reducer with a new reducer that includes each Brick's reducer.

Here is an example taken from the demo app that shows how it is used:

demo-app/src/configureStore.js

import { applyMiddleware, createStore } from 'redux';
import createSagaMiddleware from 'redux-saga';
import createInitialState from './createInitialState';
import reducer from './reducer';
import hackerNews from '@modular-toolkit/demo-module';
import gists from '@modular-toolkit/other-demo-module';
import { BrickManager } from '@modular-toolkit/bricks/BrickManager';

const initialState = createInitialState();

export default () => {
    const sagaMiddleware = createSagaMiddleware();
    const store = createStore(reducer, initialState, applyMiddleware(sagaMiddleware));
    const brickManager = new BrickManager({ store, reducer, sagaMiddleware });
    brickManager.installBricks({
        'bricks.hackerNews': hackerNews,
        'bricks.gists': gists
    });
    return store;
};

This function works exactly the same as combineReducers from Dan Abramov's Redux library, except that it does not issue a warning when an initial state is encountered that contains keys for which there are no reducers defined.

Use this version of combineReducers if you want to combine reducers in your main application, while providing an initial state for Bricks that are added at a later point using the Brick Manager (e.g. when hydrating a server-side rendered page).

Here is an example taken from the demo app that shows how it is used:

demo-app/src/reducer.js

import { CHANGE_BACKGROUND_COLOR } from './actions';
import { combineReducers } from '@modular-toolkit/bricks';

export default combineReducers({
    page(state = {}, action = {}) {
        switch (action.type) {
            case CHANGE_BACKGROUND_COLOR:
                return {
                    ...state,
                    backgroundColor: action.backgroundColor
                };
            default:
                return state;
        }
    }
});

This context provider wrapper allows you to pass a BrickManager instance to deep-nested components through the React context API, similar to the Provider of react-redux.

Important: Make sure the brick provider is inside the Redux Provider (as shown in the example below), but always around all other components, especially BrowserRouter from React Router.

This example shows how to initialize Redux for your application, along with the BrickProvider:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrickProvider } from '@modular-toolkit/bricks';
import { applyMiddleware, createStore } from 'redux';
import createSagaMiddleware from 'redux-saga';

const reducer = state => state;
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
const { store, reducer, sagaMiddleware } = configureStore();

ReactDOM.render(
    <Provider store={store}>
        <BrickProvider store={store} reducer={reducer} sagaMiddleware={sagaMiddleware}>
            <h1>Hello world!</h1>
        </BrickProvider>
    </Provider>,
    document.getElementById('root')
);

Edit Bricks Demo II

The BrickProvider accepts these props:

  • store (required) – The redux store
  • sagaMiddleware (required) – The redux-saga middleware
  • reducer (optional) – The application's root reducer

This function allows you to create a higher-order container component that enhances other components, installing a brick when the component is mounted. It works the same way as the connect from react-redux.

You must wrap your application with a BrickProvider for this to work.

Example:

import React from 'react';
import { withBrick } from '@modular-toolkit/bricks';
import hackerNews, { HackerNews } from '@modular-toolkit/demo-module';

function MyComponent() {
    return (
        <div>
            Check out the latest Hacker News:
            <HackerNews />
        </div>
    );
}

export const enhance = withBrick('bricks.hackerNews', hackerNews);

export default enhance(MyComponent);

Edit Bricks Demo II

The withBrick function accepts three arguments:

  • storePath (required, string) – The path to the part of the global Redux state that the Brick is working with; path segments are separated by dots; example: bricks.hackerNews
  • module (required, object) – The brick module that contains store, reducer and saga of the brick; this is usually the default export of a brick package
  • initialState (optional, object) – An initial state that can be used for configuration (see below)

This function allows you to create a higher-order container component that enhances other components, installing multiple bricks when the component is mounted. It works the same way as the connect from react-redux.

You must wrap your application with a BrickProvider for this to work.

This is basically the same as withBrick, except that you can install multiple bricks instead of just one by providing an object where the keys are store paths and the values are brick modules.

Example:

import React from 'react';
import { connect } from 'react-redux';
import { withBricks } from '@modular-toolkit/bricks';
import hackerNews, { HackerNews } from '@modular-toolkit/demo-module';
import gists, { Gists } from '@modular-toolkit/other-demo-module';

function MyComponent() {
    return (
        <div>
            Check out the latest Hacker News:
            <HackerNews />
            …and the latest Gists:
            <Gists />
        </div>
    );
}

export const enhance = withBricks({
    'bricks.hackerNews': hackerNews,
    'bricks.gists': gists
});

export default enhance(MyComponent);

The withBricks function accepts one argument:

  • bricks (required, object) – an object where the keys are store paths and the values are brick modules

Passing an Initial State

In some cases, it is desirable to configure the initial state of a Brick, to pass general configuration that needs to be available when the Brick is installed.

For example, you may want to configure the base URL of an API used by a Saga to fetch data.

This can be done by passing an initial state object to the Brick Manager.

Using BrickManager.installBrick

brickManager.installBrick('bricks.hackerNews', hackerNews, {
    baseUrl: 'https://hackernews.com/api'
});

Using BrickManager.installBricks

brickManager.installBricks({
    'bricks.hackerNews': { ...hackerNews, initialState: { baseUrl: 'https://hackernews.com/api' } },
    'bricks.gists': { ...gists, initialState: { baseUrl: 'https://gists.com/api' } }
});

Using withBrick

const enhance = withBrick('bricks.hackerNews', hackerNews, {
    baseUrl: 'https://hackernews.com/api'
});

Using withBricks

const enhance = withBricks({
    'bricks.hackerNews': { ...hackerNews, initialState: { baseUrl: 'https://hackernews.com/api' } },
    'bricks.gists': { ...gists, initialState: { baseUrl: 'https://gists.com/api' } }
});

Experimental API

React version 16.7.0-alpha.0 introduces a revolutionary new feature called Hooks, which makes integrating bricks in your application much easier.

Note that the following functions only work with the React alpha version noted above. They are not recommended for production use yet – proceed at your own risk.

This Hook function allows you to install a brick from within a function React component.

You must wrap your application with a BrickProvider for this to work.

This is a Hook version of withBrick.

Example:

import React from 'react';
import hackerNews, { HackerNews } from '@modular-toolkit/demo-module';
import { useBrick } from '@modular-toolkit/bricks';

export default () => {
    useBrick('bricks.hackerNews', hackerNews);
    return (
        <div>
            Check out the latest Hacker News:
            <HackerNews />
        </div>
    );
};

Edit Bricks Demo IV

As you can see in the example, you do not need to use an enhancer to create a higher-order component. All you need is call the useBrick hook and the Hacker News brick is installed in your application's Redux setup and ready to use.

The useBrick function accepts two arguments:

  • storePath (required, string) – The path to the part of the global Redux state that the Brick is working with; path segments are separated by dots; example: bricks.hackerNews
  • module (required, object) – The brick module that contains store, reducer and saga of the brick; this is usually the default export of a brick package

This Hook function allows you to install a brick from within a function React component.

You must wrap your application with a BrickProvider for this to work.

This is a Hook version of withBricks.

Example:

import React from 'react';
import hackerNews, { HackerNews } from '@modular-toolkit/demo-module';
import gists, { Gists } from '@modular-toolkit/other-demo-module';
import { useBricks } from '@modular-toolkit/bricks';

export default () => {
    useBricks({
        'bricks.hackerNews': hackerNews,
        'bricks.gists': gists
    });
    return (
        <div>
            Check out the latest Hacker News:
            <HackerNews />
            …and the latest Gists:
            <Gists />
        </div>
    );
};

The useBricks function accepts one argument:

  • bricks (required, object) – an object where the keys are store paths and the values are brick modules

Troubleshooting

Console Warning: “Unexpected key XXX found in previous state received by the reducer”

If you get this warning in your browser console, you're using combineReducers from the Redux library. Use combineReducers from modular-toolkit instead (see above).

Change Log

Contribution Guidelines

Acknowledgements

  • Includes code taken from Redux (combineReducers)
    MIT licensed
    Copyright © 2015–present Dan Abramov
  • The BrickManager module is inspired by Reedux
    MIT licensed
    Copyright © 2017 Silviu Marian

License

MIT licensed

Copyright © 2018 mobile.de GmbH