├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── app ├── .babelrc ├── index.html ├── js │ ├── components │ │ └── Input.js │ ├── containers │ │ └── App.js │ ├── index.js │ ├── reducers │ │ ├── Elements.js │ │ └── index.js │ └── store │ │ └── configureStore.js ├── package.json ├── static │ └── bundle.js └── webpack.config.js ├── index.js ├── lib └── index.js ├── package.json └── src └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015","stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | ./test/*.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "quotes": [ 4 | 2, 5 | "single" 6 | ], 7 | "linebreak-style": [ 8 | 2, 9 | "unix" 10 | ], 11 | "semi": [ 12 | 2, 13 | "always" 14 | ], 15 | "consistent-return": 0, 16 | "no-else-return": 0, 17 | "no-console": 0, 18 | "max-len": [1, 160], 19 | "quote-props": [1, "consistent-as-needed"], 20 | "no-cond-assign": [2, "except-parens"], 21 | "radix": 0, 22 | "func-names": 0, 23 | "padded-blocks": 0, 24 | "computed-property-spacing": [2, "always"], 25 | "space-infix-ops": 0, 26 | "no-unused-vars": [1, {"vars": "local", "args": "none"}], 27 | "space-before-function-paren": 2, 28 | "space-in-parens": [2, "always"], 29 | "key-spacing": [2, { "align": "colon", "beforeColon": false, "afterColon": true }], 30 | "brace-style": [2, "allman", { "allowSingleLine": true }] 31 | }, 32 | "env": { 33 | "es6": true, 34 | "browser": true, 35 | "node": true 36 | }, 37 | "extends": "airbnb/base", 38 | "globals": { 39 | "describe": true, 40 | "it": true, 41 | "beforeEach": true 42 | }, 43 | "plugins": [ 44 | "react" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /lib 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintignore 3 | .eslintrc 4 | .gitignore 5 | .travis.yml 6 | npm-debug.log 7 | test/ 8 | app/ 9 | src/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvraamMavridis/redux-perf-middleware/dda01fb1482317d14f3761738f9f52c3bbaab551/.travis.yml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redux-perf-middleware 2 | 3 | [![npm version](https://badge.fury.io/js/redux-perf-middleware.svg)](https://badge.fury.io/js/redux-perf-middleware) [![CocoaPods](https://img.shields.io/cocoapods/l/AFNetworking.svg)]() 4 | [![semantic-versioning](https://img.shields.io/badge/semantic%20-versioning-green.svg)]() 5 | 6 | 7 | Measure the time that the actions need to change the state 8 | 9 | ### Install 10 | [![NPM](https://nodei.co/npm/redux-perf-middleware.png?mini=true)](https://nodei.co/npm/redux-perf-middleware/) 11 | 12 | ### Usage 13 | 14 | ```javascript 15 | import perflogger from 'redux-perf-middleware'; 16 | 17 | const createStoreWithMiddleware = applyMiddleware( perflogger )(createStore); 18 | const store = createStoreWithMiddleware(reducer); 19 | ``` 20 | 21 | This project adheres to [Semantic Versioning](http://semver.org/). 22 | 23 | 24 | 25 | ### Example 26 | 27 | **Dumb Reducer** 28 | 29 | ```javascript 30 | function slow(){ 31 | let sum; 32 | for(let i = 0; i< 10000; i++){ 33 | for(let j = 0; j< 10000; j++) 34 | { 35 | sum = i+j; 36 | } 37 | } 38 | return sum; 39 | } 40 | 41 | 42 | export const Elements = function ( state = {}, action ) { 43 | 44 | switch ( action.type ) 45 | { 46 | case 'SLOW': 47 | return slow(); 48 | 49 | default: 50 | return state; 51 | } 52 | }; 53 | ``` 54 | 55 | **Dumb Component** 56 | 57 | ```javascript 58 | import React, { Component } from 'react'; 59 | import { connect } from 'react-redux'; 60 | 61 | class Input extends Component { 62 | /** 63 | * Renders the markup for the topbar 64 | */ 65 | render() { 66 | const { dispatch } = this.props; 67 | return ( 68 | dispatch({ type: 'SLOW' })} /> 69 | ); 70 | } 71 | }; 72 | 73 | const selector = function( { default: elements } ){ 74 | return elements; 75 | } 76 | 77 | export default connect(selector)( Input ); 78 | ``` 79 | 80 | On every keydown **Redux** will dispatch the action with type SLOW, and in the console the middleware will log something like: 81 | 82 | ![perflogger](http://oi68.tinypic.com/2h37fqb.jpg) 83 | 84 | Or check the [sample app](https://github.com/AvraamMavridis/redux-perf-middleware/tree/master/app) 85 | 86 | ## Contributing 87 | Feel free to open issues, make suggestions or send PRs. 88 | This project adheres to the Contributor Covenant [code of conduct](http://contributor-covenant.org/). By participating, you are expected to uphold this code. 89 | 90 | ## Contact 91 | 92 | Twitter: [@avraamakis](https://twitter.com/avraamakis) 93 | 94 | ### License 95 | MIT 96 | -------------------------------------------------------------------------------- /app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015","react","stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/js/components/Input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple Input Component 3 | * 4 | * @author Avraam Mavridis 5 | * 6 | */ 7 | import React, { Component } from 'react'; 8 | import { connect } from 'react-redux'; 9 | 10 | class Input extends Component { 11 | /** 12 | * Renders the markup for the topbar 13 | */ 14 | render() { 15 | const { dispatch } = this.props; 16 | return ( 17 | dispatch({ type: 'SLOW' })} /> 18 | ); 19 | } 20 | }; 21 | 22 | const selector = function( { default: elements } ){ 23 | return elements; 24 | } 25 | 26 | export default connect(selector)( Input ); 27 | -------------------------------------------------------------------------------- /app/js/containers/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main Component, Container 3 | * 4 | * @author Avraam Mavridis 5 | * 6 | */ 7 | 'use strict'; 8 | 9 | /** External Dependecies */ 10 | import React from 'react'; 11 | import { Provider } from 'react-redux'; 12 | import { connect } from 'react-redux'; 13 | import configureStore from '../store/configureStore'; 14 | import Input from '../components/Input'; 15 | const store = configureStore(); 16 | 17 | export class App extends React.Component 18 | { 19 | render() { 20 | return ( 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/js/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { App } from './containers/App'; 4 | 5 | ReactDOM.render( , document.getElementById( 'main' ) ); 6 | -------------------------------------------------------------------------------- /app/js/reducers/Elements.js: -------------------------------------------------------------------------------- 1 | function slow(){ 2 | let sum; 3 | for(let i = 0; i< 10000; i++){ 4 | for(let j = 0; j< 10000; j++) 5 | { 6 | sum = i+j; 7 | } 8 | } 9 | return sum; 10 | } 11 | 12 | 13 | export const Elements = function ( state = {}, action ) { 14 | 15 | switch ( action.type ) 16 | { 17 | case 'SLOW': 18 | return slow(); 19 | 20 | default: 21 | return state; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /app/js/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { Elements } from './Elements'; 2 | import { combineReducers } from 'redux'; 3 | 4 | export default combineReducers( { 5 | elements: Elements 6 | } ); 7 | -------------------------------------------------------------------------------- /app/js/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; 2 | import * as reducers from '../reducers/index'; 3 | import perflogger from 'redux-perf-middleware'; 4 | 5 | let createStoreWithMiddleware = applyMiddleware( perflogger )( createStore ); 6 | 7 | const rootReducer = combineReducers( reducers ); 8 | 9 | export default function configureStore( initialState = {} ) { 10 | return createStoreWithMiddleware( rootReducer, initialState ); 11 | } 12 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --config webpack.config.js", 8 | "build-dev": "webpack -p", 9 | "test": "mocha --compilers js:babel-core/register --recursive", 10 | "test:watch": "npm test -- --watch" 11 | }, 12 | "author": "Avraam Mavridis (http://avraammavridis.github.io/)", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "babel-core": "^6.4.0", 16 | "babel-loader": "^6.2.1", 17 | "babel-preset-es2015": "^6.3.13", 18 | "babel-preset-stage-0": "^6.3.13", 19 | "eslint": "^1.10.3", 20 | "eslint-config-airbnb": "^3.1.0", 21 | "eslint-plugin-react": "^3.15.0", 22 | "open-browser-webpack-plugin": "0.0.2", 23 | "react": "^0.14.6", 24 | "react-dom": "^0.14.6", 25 | "react-hot-loader": "^1.3.0", 26 | "react-redux": "^4.0.6", 27 | "redux": "^3.0.5", 28 | "redux-perf-middleware": "0.0.3", 29 | "webpack": "^1.12.11", 30 | "webpack-dev-server": "^1.14.1" 31 | }, 32 | "dependencies": { 33 | "babel-preset-react": "^6.3.13" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | const OpenBrowserPlugin = require( 'open-browser-webpack-plugin' ); 3 | 4 | var devFlagPlugin = new webpack.DefinePlugin({ 5 | __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false')) 6 | }); 7 | 8 | module.exports = { 9 | entry: [ 10 | 'webpack-dev-server/client?http://localhost:8080', 11 | 'webpack/hot/only-dev-server', 12 | './js/index.js' 13 | ], 14 | output: { 15 | path: __dirname + '/static/', 16 | publicPath: '/static/', 17 | filename: 'bundle.js', 18 | hot: true 19 | }, 20 | plugins: [ 21 | new webpack.HotModuleReplacementPlugin(), 22 | new OpenBrowserPlugin( { url: 'http://localhost:8080' } ) 23 | ], 24 | module: { 25 | loaders: [ 26 | { test: /\.js$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }, 27 | ] 28 | }, 29 | resolve: { 30 | extensions: ['', '.js', '.json'] 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require( './lib' ); 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | /** 7 | * Redux performance logger 8 | * 9 | * @author Avraam Mavridis 10 | * 11 | */ 12 | var perflogger = exports.perflogger = function perflogger(store) { 13 | return function (next) { 14 | return function (action) { 15 | console.log('dispatching', action); 16 | console.log('%c Dispatching ', 'background: #222; color: #bada55', action); 17 | var start = performance.now(); 18 | var result = next(action); 19 | var end = performance.now(); 20 | console.log('%c Action with type "' + action.type + '" took ' + (end - start).toFixed(2) + ' milliseconds.', 'background: #bada55; color: #222'); 21 | return result; 22 | }; 23 | }; 24 | }; 25 | 26 | exports.default = perflogger; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-perf-middleware", 3 | "version": "1.2.2", 4 | "description": "Measure the time that actions needs to change the state", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm run compile && mocha --compilers js:babel-register --require babel-polyfill", 8 | "compile": "babel --presets es2015,stage-0 --require babel-polyfill -d lib/ src/", 9 | "prepublish": "npm run compile", 10 | "watch": "npm-scripts-watcher" 11 | }, 12 | "watch": { 13 | "src/**/*.js": [ 14 | "compile" 15 | ] 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/AvraamMavridis/redux-perf-middleware.git" 20 | }, 21 | "keywords": [ 22 | "redux", 23 | "logger", 24 | "redux-logger", 25 | "redux", 26 | "middleware", 27 | "redux-perf-logger", 28 | "redux-perf-middleware", 29 | "redux-performance", 30 | "performance" 31 | ], 32 | "author": "Avraam Mavridis (http://avraammavridis.github.io/)", 33 | "license": "MIT", 34 | "devDependencies": { 35 | "babel-cli": "^6.11.4", 36 | "babel-core": "^6.11.4", 37 | "babel-plugin-transform-decorators": "^6.8.0", 38 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 39 | "babel-polyfill": "^6.9.1", 40 | "babel-preset-es2015": "^6.9.0", 41 | "babel-preset-react": "^6.11.1", 42 | "babel-preset-stage-0": "^6.5.0", 43 | "babel-register": "^6.11.6", 44 | "chai": "^3.5.0", 45 | "eslint": "^1.10.3", 46 | "eslint-config-airbnb": "^2.0.0", 47 | "eslint-config-google": "^0.3.0", 48 | "eslint-plugin-react": "^3.11.2", 49 | "mocha": "^3.0.0", 50 | "promise": "^7.1.1", 51 | "sinon": "^1.17.5", 52 | "sinon-chai": "^2.8.0" 53 | }, 54 | "bugs": { 55 | "url": "https://github.com/AvraamMavridis/redux-perf-middleware/issues" 56 | }, 57 | "homepage": "https://github.com/AvraamMavridis/redux-perf-middleware#readme", 58 | "dependencies": { 59 | "present": "^1.0.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Redux performance logger 3 | * 4 | * @author Avraam Mavridis 5 | * 6 | */ 7 | 8 | const present = require('present') 9 | 10 | export const perflogger = store => next => action => { 11 | console.log( '%c Dispatching ', 'background: #222; color: #bada55', action ); 12 | const start = present(); 13 | const result = next( action ); 14 | const end = present(); 15 | console.log( `%c Action with type "${action.type}" took ${( end-start ).toFixed( 2 )} milliseconds.`, 'background: #bada55; color: #222' ); 16 | return result; 17 | }; 18 | 19 | export default perflogger; 20 | --------------------------------------------------------------------------------