reductor
TypeScript icon, indicating that this package has built-in type declarations

0.8.10 • Public • Published

Reductor

Build Status npm

An opinionated javascript framework for scalable applications with React and Redux.

"If my calculations are correct, when this baby hits 88 miles per hour... you're gonna see some serious shit."

Install

To install the stable version:

npm install reductor --save

Most likely you'd also want to install React and React Router

npm install react react-router --save 

The Gist

Reductor tries to make developing React and Redux based applications easier by reducing boilerplate and providing a simple and fluid API to set up a modular and scalable application architecture. Reductor exposes two main classes: Application and Module and one convenience function getLogger() which will give you a cool Logger 😎

Application

The Application, you've guessed it, is the heart of everything. It is basically a wrapper around the Redux store and a Module registry. It exposes methods to configure your Redux store and set up your routing.

// Framework Imports
import { Application } from 'reductor';
import React from 'react';
import { Route, IndexRedirect } from 'react-router';
 
// Constants for Development purposes
const ENV = process.env.NODE_ENV || 'development';
const DevTools = window.devToolsExtension ? window.devToolsExtension() : f => f;
 
// Import all modules that you want to load here
import {
  CounterModule,
  PageModule,
  DashboardModule,
} from 'modules';
 
// Bootstrap the application
const ReductorApp = new Application();
 
ReductorApp
  .name('Reductor')
  .version('1.0.0');
 
// In development mode enable Redux DevTools and debug mode.
if (ENV === 'development') {
  ReductorApp
    .addEnhancer(DevTools)
    .debug();
}
 
// Add Modules to the Application instance
ReductorApp
  .addModule(CounterModule)
  .addModule(PageModule)
  .addModule(DashboardModule);
 
// Define Routes
ReductorApp.setRoutes(
  <Route>
    <Route path="/" component={CounterModule.getComponent('LandingPage')} />
    <Route path="/:locator" component={CounterModule.getComponent('SessionContainer')}>
      <IndexRedirect to="login" />
      <Route path="login" component={CounterModule.getComponent('LoginContainer')} />
      <Route component={PageModule.getComponent('PageContainer')}>
        <Route path="dashboard" component={DashboardModule.getComponent('DashboardContainer')} />
      </Route>
    </Route>
  </Route>
);
 
// Run the application
// This will basically tie everything together
ReductorApp.run();

Module

A module is a bundle of React components, reducers and action creators (together with payload and meta creators if needed). A module takes care of registering components and connecting them to the module's state (or full application state via selectors) using the react-redux bindings. It further maps actions to their respective reducers and will automatically create self dispatching action creators (by leveraging redux-actions. Optionally you can also assign payload and meta creators

import { Module } from 'reductor';
import CounterComponent from './CounterComponent.jsx';
 
// Initialize a new module
const CounterModule = new Module();
 
// Bootstrap a Module
// The module's name will be transformed into snake case and used as the key
// in the redux state. This can be overridden by calling setKey('myKey') on the
// module. 
CounterModule
  .name('Counter')
  .version('1.0.0');
 
// Set the module's initial state
CounterModule.setInitialState({ counter: 0 });
 
// Register a component with the module
// The first argument is the name that is used
// to retrieve the componen via Module.getComponent().
// The second is the component itself.
// It takes a selector map as third argument which allows to connect it to
// different parts of the application's state and pass in functions that map the
// state to the components props. This allows use of libraries like reselect etc.
// The 4th and last argument is a boolean value. If that's set to false, the component
// will not be connected to redux at all. By default the complete state of the module is
// passed into the components props.
CounterModule
  .component('Counter', CounterComponent);
 
const IncrementReducer = (state, action) => (
  { ...state, counter: state.counter + action.payload }
);
 
const DecrementReducer = (state, action) => (
  { ...state, counter: state.counter - action.payload }
);
 
// The on method will link action types to reducers and will create action creators for you.
// By default the first argument takes an arbitrary string that will be used to generate an action type.
// Action types will always be normalized to snake case and then all uppercase: INCREMENT_COUNTER
// The action creators will be camelcase: incrementCounter() 
// It takes an optional payload creator as 3rd and meta creator as 4th argument.
// Usually all async stuff should be put into a payload creator.
// The action creators are self dispatching and will be passed down to all connected components via
// an 'actions' prop. That means from your component you can simply call
// this.props.actions.incrementCounter(5) to dispatch the INCREMENT_COUNTER action and increment my 
// counter by 5.
CounterModule
  .on('Increment Counter', IncrementReducer);
  .on('Decrement Counter', DecrementReducer);
 
export default CounterModule;

How do I do XYZ ?

Please feel free to ask me any questions and I will extend this README with tips, tricks, techniques, answers and unicorns.

You can simply open an issue or contact me here:

Collaboration

Everyone is encouraged and invited to help work on reductor, open issues and submit pull requests. Because this project is going to be used in a productive enterprise application, which is deployed to multiple customers we need to follow a rather strict release process to ensure code quality and robustness.

Workflow

  1. Open an issue with the appropriate flags. (See Issues)
  2. Wait for product council to appove the ticket (will be tagged approved and moved into the Accepted column.
  3. Start working on your ticket by assigning it to you and moving it into the In Progress column.
  4. When done open a pull request.
  5. Product council will move the ticket into code review.
  6. Fix any issues raised by product council
  7. When code review is done, your pull request will be merged.

Issues

  • Bug - a description of a bug in the current code that needs to be fixed.
    • Please add steps to reproduce
  • Story - a new feature request for functionality that currently does not exist
    • Consider using the As a [role], I want to [do something] so that [reason/benefit] format to describe your story.
    • Add specific requirements as bulletpoints to add more detail.
  • Epic - a higher order ticket that outlines a bigger goal that needs to be discussed and where work needs to be identified.

Each ticket should also be assigned one of the following tags:

  • Velocity - improves developer team velocity/experience
  • Infrastucture - improves infrastructure (server, package managment, dependencies)
  • Test - improves test coverage or test workflow
  • Docs - improves documentation

Project Management

I'm using ZenHub for the project management on this repository.

Code Style

Please follow Airbnb's JS Style Guide

Branching model

Please fork the repository and follow the Git Flow branching model. Please include ticket ID's in your feature banche name: feature/reductor-34/my_awesome_feature.

GIT commit messages

  • Use the present tense ("Add feature" not "Added feature")
  • Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
  • Limit the first line to 72 characters or less
  • Reference issues and pull requests liberally
  • Consider starting the commit message with an applicable emoji:
    • 💄 :lipstick: when improving the format/structure of the code
    • ⚡️ :zap: when improving performance
    • 📝 :memo: when writing docs
    • 🐛 :bug: when fixing a bug
    • 💥 :boom: when introducing breaking changes
    • 🔥 :fire: when removing code or files
    • 💚 :green_heart: when fixing the CI build
    • :white_check_mark: when adding tests
    • 🔒 :lock: when dealing with security
    • ⤴️ :arrow_heading_up: when upgrading dependencies
    • ⤵️ :arrow_heading_down: when downgrading dependencies
    • :sparkles: when adding a new feature
    • 💩 :shit: when fixing eslint issues or other shitty code
    • :trollface: :trollface: ... you know when ...
    • :squirrel: :squirrel: when releasing a new version

Thanks ...

... to all the bright people in the react and redux community for creating amazing libraries to make my life easier and allowing me to create this framework. Most of all Dan Abramov for completely changing how I thought about developing UI's and managing state.

Package Sidebar

Install

npm i reductor

Weekly Downloads

39

Version

0.8.10

License

MIT

Last publish

Collaborators

  • mbensch