diff --git a/.gitignore b/.gitignore index 1a616b9..2485a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,7 @@ typings/ # next.js build output .next -.idea +.idea/ runs/ dist/ lib/ diff --git a/package.json b/package.json index ac23401..18798dd 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "initialize": "node ./setupRuns.js", "start": "node ./runBenchmarks.js", - "test": "jest" + "test": "jest", + "format" : "prettier --write \"sources/**/src/*.{js,jsx}\" *.js" }, "repository": { "type": "git", @@ -30,5 +31,8 @@ "puppeteer": "^1.7.0", "recursive-copy": "^2.0.9", "tracealyzer": "^0.9.3" + }, + "devDependencies": { + "prettier": "^1.16.4" } } diff --git a/react-redux-versions/react-redux-7.0.0.alpha-3.min.js b/react-redux-versions/react-redux-7.0.0.alpha-3.min.js new file mode 100644 index 0000000..466e3b4 --- /dev/null +++ b/react-redux-versions/react-redux-7.0.0.alpha-3.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react-dom"),require("react"),require("redux")):"function"==typeof define&&define.amd?define(["exports","react-dom","react","redux"],t):t(e.ReactRedux={},e.ReactDOM,e.React,e.Redux)}(this,function(e,o,E,r){"use strict";var M="default"in E?E.default:E;function i(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function t(e,t){return e(t={exports:{}},t.exports),t.exports}function n(){}var s=t(function(e){e.exports=function(){function e(e,t,r,n,o,i){if("SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"!==i){var s=Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}var r={array:e.isRequired=e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t};return r.checkPropTypes=n,r.PropTypes=r}()}),m=M.createContext(null),u=null,a={notify:function(){}};var T=function(){function e(e,t){this.store=e,this.parentSub=t,this.unsubscribe=null,this.listeners=a,this.handleChangeWrapper=this.handleChangeWrapper.bind(this)}var t=e.prototype;return t.addNestedSub=function(e){return this.trySubscribe(),this.listeners.subscribe(e)},t.notifyNestedSubs=function(){this.listeners.notify()},t.handleChangeWrapper=function(){this.onStateChange&&this.onStateChange()},t.isSubscribed=function(){return!!this.unsubscribe},t.trySubscribe=function(){var r,n;this.unsubscribe||(this.unsubscribe=this.parentSub?this.parentSub.addNestedSub(this.handleChangeWrapper):this.store.subscribe(this.handleChangeWrapper),this.listeners=(r=[],n=[],{clear:function(){r=n=u},notify:function(){var t=r=n;o.unstable_batchedUpdates(function(){for(var e=0;e. You may also pass a {context : MyContext} option to connect");var R=h;return function(S){var e=S.displayName||S.name||"Component",g=i(e),O=j({},b,{getDisplayName:i,methodName:s,renderCountProp:u,shouldHandleStateChanges:x,storeKey:c,displayName:g,wrappedComponentName:e,WrappedComponent:S}),t=b.pure;var C=t?E.useMemo:function(e){return e()};function r(e){var t=E.useMemo(function(){return[e.context,e.forwardedRef,_(e,["context","forwardedRef"])]},[e]),r=t[0],n=t[1],o=t[2],i=E.useMemo(function(){return r&&r.Consumer&&N(M.createElement(r.Consumer,null))?r:R},[r,R]),s=E.useContext(i);$(e.store||s,'Could not find "store" in the context of "'+g+'". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to '+g+" in connect options.");var u=e.store||s.store,a=E.useMemo(function(){return w(u.dispatch,O)},[u]),c=E.useMemo(function(){if(!x)return F;var e=new T(u,s.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]},[u,s.subscription]),p=c[0],f=c[1],d=E.useMemo(function(){return j({},s,{subscription:p})},[s,p]),l=E.useReducer(U,D,W),y=l[0][0],h=l[1];if(y&&y.error)throw y.error;var b=E.useRef(),m=E.useRef(o),v=E.useRef(),P=C(function(){return v.current&&o===m.current?v.current:a(u.getState(),o)},[u,y,o]);return E.useEffect(function(){m.current=o,b.current=P,v.current&&(v.current=null,f())}),E.useEffect(function(){if(x){var n=!1,e=function(){if(!n){var e,t,r=u.getState();try{e=a(r,m.current)}catch(e){t=e}e===b.current?f():(h({type:"STORE_UPDATED",payload:{latestStoreState:r,error:t}}),v.current=b.current=e)}};p.onStateChange=e,p.trySubscribe(),e();return function(){n=!0,p.tryUnsubscribe()}}},[u,p,a]),E.useMemo(function(){var e=M.createElement(S,j({},P,{ref:n}));return x?M.createElement(i.Provider,{value:d},e):e},[i,S,P,n,d])}var n=t?M.memo(r):r;if(n.WrappedComponent=S,n.displayName=g,l){var o=M.forwardRef(function(e,t){return M.createElement(n,{wrapperProps:e,forwardedRef:t})});return o.displayName=g,o.WrappedComponent=S,q(o,S)}return q(n,S)}}var w=Object.prototype.hasOwnProperty;function x(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function R(e,t){if(x(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var r=Object.keys(e);if(r.length!==Object.keys(t).length)return!1;for(var n=0;n - version.replace('react-redux-', '').replace('.min.js', '')) - -const reduxVersions = process.env.REDUX ? process.env.REDUX.trim().split(':') : versions -const benchmarksToRun = process.env.BENCHMARKS ? process.env.BENCHMARKS.split(':') : sources -const length = process.env.SECONDS ? process.env.SECONDS : 30 -const trace = process.env.BENCHMARK_TRACE ? process.env.BENCHMARK_TRACE === "true" : true; + version.replace("react-redux-", "").replace(".min.js", "") +); + +const reduxVersions = process.env.REDUX + ? process.env.REDUX.trim().split(":") + : versions; +const benchmarksToRun = process.env.BENCHMARKS + ? process.env.BENCHMARKS.split(":") + : sources; +const length = process.env.SECONDS ? process.env.SECONDS : 30; +const trace = process.env.BENCHMARK_TRACE + ? process.env.BENCHMARK_TRACE === "true" + : true; // Given an array of items such as ["a", "b", "c", "d"], return the pairwise entries // in the form [ ["a","b"], ["b","c"], ["c","d"] ] function pairwise(list) { - // Create a new list offset by 1 - var allButFirst = _.rest(list); - // Pair up entries at each index - var zipped = _.zip(list, allButFirst); - // Remove last entry, as there's a mismatch from the offset - var pairwiseEntries = _.initial(zipped); - return pairwiseEntries; + // Create a new list offset by 1 + var allButFirst = _.rest(list); + // Pair up entries at each index + var zipped = _.zip(list, allButFirst); + // Remove last entry, as there's a mismatch from the offset + var pairwiseEntries = _.initial(zipped); + return pairwiseEntries; } -async function runBenchmarks() { - for (let j = 0; j < benchmarksToRun.length; j++) { - const benchmark = benchmarksToRun[j] +function printBenchmarkResults(benchmark, versionPerfEntries) { + console.log(`\nResults for benchmark ${benchmark}:`); - const versionPerfEntries = {}; + let traceCategories = []; - const source = join(__dirname, 'runs', benchmark) - console.log(`Running benchmark ${benchmark}`) + if (trace) { + traceCategories = ["Scripting", "Rendering", "Painting"]; + } + const table = new Table({ + head: [ + "Version", + "Avg FPS", + "Render\n(Mount, Avg)", + ...traceCategories, + "FPS Values" + ] + }); + + Object.keys(versionPerfEntries) + .sort() + .forEach(version => { + const versionResults = versionPerfEntries[version]; - for (let i = 0; i < reduxVersions.length; i++) { - const version = reduxVersions[i] - const toRun = join(source, version) - console.log(` react-redux version: ${version}`) - const browser = await puppeteer.launch({ - //headless: false - }); + const { fps, profile, mountTime, averageUpdateTime } = versionResults; - const URL = "http://localhost:9999"; - try { - const sourceFilePath = join(VERSIONS_FOLDER, `react-redux-${version}.min.js`); - const destFilePath = join(source, "react-redux.min.js") - copyFileSync(sourceFilePath, destFilePath); + let traceResults = []; - const server = await serverUtils.runServer(9999, source); + if (trace) { + traceResults = [ + profile.categories.scripting.toFixed(2), + profile.categories.rendering.toFixed(2), + profile.categories.painting.toFixed(2) + ]; + } - console.log(` Checking max FPS... (${length} seconds)`) - const fpsRunResults = await serverUtils.capturePageStats(browser, URL, null, length * 1000); + const fpsNumbers = fps.values.map(entry => entry.FPS); - let traceRunResults, categories; + table.push([ + version, + fps.weightedFPS.toFixed(2), + `${mountTime.toFixed(1)}, ${averageUpdateTime.toFixed(1)}`, + ...traceResults, + fpsNumbers.toString() + ]); + }); - if(trace) { - console.log(` Running trace... (${length} seconds)`); - const traceFilename = join(__dirname, 'runs', `trace-${benchmark}-${version}.json`) - traceRunResults = await serverUtils.capturePageStats(browser, URL, traceFilename, length * 1000); - } + console.log(table.toString()); +} - const {fpsValues, start, end} = fpsRunResults; +function calculateBenchmarkStats(fpsRunResults, categories, traceRunResults) { + const { fpsValues, start, end } = fpsRunResults; - if(trace) { - categories = traceRunResults.traceMetrics.profiling.categories; - } + if (trace) { + categories = traceRunResults.traceMetrics.profiling.categories; + } - // skip first value = it's usually way lower due to page startup - const fpsValuesWithoutFirst = fpsValues.slice(1); - const lastEntry = _.last(fpsValues); + // skip first value = it's usually way lower due to page startup + const fpsValuesWithoutFirst = fpsValues.slice(1); + const lastEntry = _.last(fpsValues); - const averageFPS = fpsValuesWithoutFirst.reduce((sum, entry) => sum + entry.FPS, 0) / fpsValuesWithoutFirst.length || 1; + const averageFPS = + fpsValuesWithoutFirst.reduce((sum, entry) => sum + entry.FPS, 0) / + fpsValuesWithoutFirst.length || 1; - const pairwiseEntries = pairwise(fpsValuesWithoutFirst); + const pairwiseEntries = pairwise(fpsValuesWithoutFirst); - const fpsValuesWithDurations = pairwiseEntries.map(pair => { - const [first, second] = pair; - const duration = second.timestamp - first.timestamp; - const durationSeconds = duration / 1000.0 + const fpsValuesWithDurations = pairwiseEntries.map(pair => { + const [first, second] = pair; + const duration = second.timestamp - first.timestamp; + const durationSeconds = duration / 1000.0; - return {FPS : first.FPS, durationSeconds} - }) + return { FPS: first.FPS, durationSeconds }; + }); - const sums = fpsValuesWithDurations.reduce( (prev, current) => { - const weightedFPS = current.FPS * current.durationSeconds; + const sums = fpsValuesWithDurations.reduce( + (prev, current) => { + const weightedFPS = current.FPS * current.durationSeconds; - return { - weightedFPS : prev.weightedFPS + weightedFPS, - durationSeconds : prev.durationSeconds + current.durationSeconds, - } - }, {FPS : 0, weightedFPS : 0, durationSeconds : 0}); + return { + weightedFPS: prev.weightedFPS + weightedFPS, + durationSeconds: prev.durationSeconds + current.durationSeconds + }; + }, + { FPS: 0, weightedFPS: 0, durationSeconds: 0 } + ); + const weightedFPS = sums.weightedFPS / sums.durationSeconds; - const weightedFPS = sums.weightedFPS / sums.durationSeconds; + const fps = { averageFPS, weightedFPS, values: fpsValuesWithoutFirst }; - const fps = {averageFPS, weightedFPS, values : fpsValuesWithoutFirst} + const { reactTimingEntries } = fpsRunResults; + const [mountEntry, ...updateEntries] = reactTimingEntries; - const {reactTimingEntries} = fpsRunResults; + const mountTime = mountEntry.actualTime; - const [mountEntry, ...updateEntries] = reactTimingEntries; + const averageUpdateTime = + updateEntries.reduce((sum, entry) => sum + entry.actualTime, 0) / + updateEntries.length || 1; - const mountTime = mountEntry.actualTime; + return { fps, profile: { categories }, mountTime, averageUpdateTime }; +} - const averageUpdateTime = updateEntries.reduce((sum, entry) => sum + entry.actualTime, 0) / updateEntries.length || 1; +async function runBenchmarks() { + for (let j = 0; j < benchmarksToRun.length; j++) { + const benchmark = benchmarksToRun[j]; - versionPerfEntries[version] = {fps, profile : {categories}, mountTime, averageUpdateTime}; + const versionPerfEntries = {}; - server.close(); - } catch (e) { - console.error(e) - process.exit(-1) - } finally { - await browser.close() - } - } + const source = join(__dirname, "runs", benchmark); + console.log(`Running benchmark ${benchmark}`); - console.log(`\nResults for benchmark ${benchmark}:`); + for (let i = 0; i < reduxVersions.length; i++) { + const version = reduxVersions[i]; + const toRun = join(source, version); + console.log(` react-redux version: ${version}`); + const browser = await puppeteer.launch({ + //headless: false + }); - let traceCategories = []; + const URL = "http://localhost:9999"; + try { + const sourceFilePath = join( + VERSIONS_FOLDER, + `react-redux-${version}.min.js` + ); + const destFilePath = join(source, "react-redux.min.js"); + copyFileSync(sourceFilePath, destFilePath); - if(trace) { - traceCategories = ['Scripting', 'Rendering', 'Painting']; - } + const server = await serverUtils.runServer(9999, source); - const table = new Table({ - head: ['Version', 'Avg FPS', "Render\n(Mount, Avg)", ...traceCategories, 'FPS Values'] - }); + console.log(` Checking max FPS... (${length} seconds)`); + const fpsRunResults = await serverUtils.capturePageStats( + browser, + URL, + null, + length * 1000 + ); - Object.keys(versionPerfEntries).sort().forEach(version => { - const versionResults = versionPerfEntries[version]; + let traceRunResults, categories; - const {fps, profile, mountTime, averageUpdateTime,} = versionResults; + if (trace) { + console.log(` Running trace... (${length} seconds)`); + const traceFilename = join( + __dirname, + "runs", + `trace-${benchmark}-${version}.json` + ); + traceRunResults = await serverUtils.capturePageStats( + browser, + URL, + traceFilename, + length * 1000 + ); + } - let traceResults = []; + versionPerfEntries[version] = calculateBenchmarkStats( + fpsRunResults, + categories, + traceRunResults + ); - if(trace) { - traceResults = [ - profile.categories.scripting.toFixed(2), - profile.categories.rendering.toFixed(2), - profile.categories.painting.toFixed(2), - ] + server.close(); + } catch (e) { + console.error(e); + process.exit(-1); + } finally { + await browser.close(); } - - const fpsNumbers = fps.values.map(entry => entry.FPS); - - table.push([ - version, - fps.weightedFPS.toFixed(2), - `${mountTime.toFixed(1)}, ${averageUpdateTime.toFixed(1)}`, - ...traceResults, - fpsNumbers.toString() - ]) - }); - - console.log(table.toString()) + } + printBenchmarkResults(benchmark, versionPerfEntries); } - - process.exit(0) + process.exit(0); } -runBenchmarks() \ No newline at end of file +runBenchmarks(); diff --git a/setupRuns.js b/setupRuns.js index 95dd3be..86656f7 100644 --- a/setupRuns.js +++ b/setupRuns.js @@ -1,76 +1,82 @@ /* eslint no-console: 0 */ -'use strict'; +"use strict"; -const { readdirSync, copyFile } = require('fs'); -const rimraf = require('rimraf'); -const { join } = require('path'); +const { readdirSync, copyFile } = require("fs"); +const rimraf = require("rimraf"); +const { join } = require("path"); const spawn = require("cross-spawn"); -const copy = require('recursive-copy') +const copy = require("recursive-copy"); -console.log('clearing out old runs...') -rimraf.sync(join(__dirname, 'runs', '*')) +console.log("clearing out old runs..."); +rimraf.sync(join(__dirname, "runs", "*")); -console.log(`installing global dependencies of all benchmarks...`) -let installTask = spawn.sync('yarn', [''], { +console.log(`installing global dependencies of all benchmarks...`); +let installTask = spawn.sync("yarn", [""], { cwd: __dirname, - stdio: 'inherit', + stdio: "inherit" }); if (installTask.status > 0) { process.exit(installTask.status); } -installTask = spawn.sync('yarn', [''], { - cwd: join(__dirname, 'fps-emit'), - stdio: 'inherit', -}) +installTask = spawn.sync("yarn", [""], { + cwd: join(__dirname, "fps-emit"), + stdio: "inherit" +}); if (installTask.status > 0) { process.exit(installTask.status); } -installTask = spawn.sync('yarn', ['build'], { - cwd: join(__dirname, 'fps-emit'), - stdio: 'inherit', -}) +installTask = spawn.sync("yarn", ["build"], { + cwd: join(__dirname, "fps-emit"), + stdio: "inherit" +}); if (installTask.status > 0) { process.exit(installTask.status); } -const sources = readdirSync(join(__dirname, 'sources')) +const sources = readdirSync(join(__dirname, "sources")); sources.forEach(benchmark => { - const src = join(__dirname, 'sources', benchmark) - let cwd - cwd = src - console.log(`installing dependencies of benchmark ${benchmark}...`) - let installTask = spawn.sync('yarn', [''], { + const src = join(__dirname, "sources", benchmark); + let cwd; + cwd = src; + console.log(`installing dependencies of benchmark ${benchmark}...`); + let installTask = spawn.sync("yarn", [""], { cwd, - stdio: 'inherit', + stdio: "inherit" }); if (installTask.status > 0) { process.exit(installTask.status); } - const filesToCopy = ["react.production.min.js", "react-dom.production.min.js", "redux.min.js"]; + const filesToCopy = [ + "react.production.min.js", + "react-dom.production.min.js", + "redux.min.js" + ]; - filesToCopy.forEach( filename => { - copyFile(join(__dirname, 'copy-to-public', filename), join(src, 'public', filename), e => { - if (e) { - console.log(e) - process.exit(1); - } - }) - }) - + filesToCopy.forEach(filename => { + copyFile( + join(__dirname, "copy-to-public", filename), + join(src, "public", filename), + e => { + if (e) { + console.log(e); + process.exit(1); + } + } + ); + }); - console.log(`building production version of benchmark ${benchmark}...`) - installTask = spawn.sync('npm', ['run', 'build'], { + console.log(`building production version of benchmark ${benchmark}...`); + installTask = spawn.sync("yarn", ["build"], { cwd, - stdio: 'inherit', + stdio: "inherit" }); if (installTask.status > 0) { process.exit(installTask.status); } - const dest = join(__dirname, 'runs', benchmark); - copy(join(src, 'build'), dest); - -}) + const dest = join(__dirname, "runs", benchmark); + copy(join(src, "build"), dest); +}); diff --git a/sources/deeptree/src/App.js b/sources/deeptree/src/App.js index 6b3fe05..3b6e517 100644 --- a/sources/deeptree/src/App.js +++ b/sources/deeptree/src/App.js @@ -1,42 +1,46 @@ -import React from 'react'; -import {connect} from "react-redux"; +import React from "react"; +import { connect } from "react-redux"; import Slice from "./Slice"; import * as c from "./constants"; -import {incrementRandomCounter} from "./counters"; +import { incrementRandomCounter } from "./counters"; let slices; -const mapState = (state) => { - if(!slices) { - slices = Object.keys(state).map(key => Number(key)) - slices.sort() - } +const mapState = state => { + if (!slices) { + slices = Object.keys(state).map(key => Number(key)); + slices.sort(); + } - return {slices}; -} + return { slices }; +}; -const mapDispatch = {incrementRandomCounter}; +const mapDispatch = { incrementRandomCounter }; class App extends React.Component { - render () { - return ( -
- -
- {this.props.slices.map((slice, idx) => { - return ( -
- -
- ) - })} -
-
- - ) - } + render() { + return ( +
+ +
+ {this.props.slices.map((slice, idx) => { + return ( +
+ +
+ ); + })} +
+
+ ); + } } App.displayName = "App"; -export default connect(mapState, mapDispatch)(App); +export default connect( + mapState, + mapDispatch +)(App); diff --git a/sources/deeptree/src/App.test.js b/sources/deeptree/src/App.test.js index a754b20..23c181f 100644 --- a/sources/deeptree/src/App.test.js +++ b/sources/deeptree/src/App.test.js @@ -1,9 +1,9 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; +import React from "react"; +import ReactDOM from "react-dom"; +import App from "./App"; -it('renders without crashing', () => { - const div = document.createElement('div'); +it("renders without crashing", () => { + const div = document.createElement("div"); ReactDOM.render(, div); ReactDOM.unmountComponentAtNode(div); }); diff --git a/sources/deeptree/src/Pair.jsx b/sources/deeptree/src/Pair.jsx index 6bbc5f8..0321c9e 100644 --- a/sources/deeptree/src/Pair.jsx +++ b/sources/deeptree/src/Pair.jsx @@ -1,44 +1,55 @@ -import React from 'react' -import { connect } from 'react-redux' +import React from "react"; +import { connect } from "react-redux"; -const mapState = (state, props) => state[props.sliceId][props.pairId] +const mapState = (state, props) => state[props.sliceId][props.pairId]; class Pair extends React.Component { - state = { - direction: 'up', - value: this.props.value - } - - static getDerivedStateFromProps(props, state) { - if (props.value === state.value) return null; - - const direction = props.value > state.value ? "up" : "down"; - - return { - value: props.value, - direction, - }; - } - - shouldComponentUpdate(nextProps) { - return this.props.value !== nextProps.value - } - - render() { - const { direction } = this.state; - - return ( -
  • - {this.props.name} - - - {this.props.value} - -
  • - ) - } + state = { + direction: "up", + value: this.props.value + }; + + static getDerivedStateFromProps(props, state) { + if (props.value === state.value) return null; + + const direction = props.value > state.value ? "up" : "down"; + + return { + value: props.value, + direction + }; + } + + shouldComponentUpdate(nextProps) { + return this.props.value !== nextProps.value; + } + + render() { + const { direction } = this.state; + + return ( +
  • + {this.props.name} + + + {this.props.value} + +
  • + ); + } } Pair.displayName = "Pair"; -export default connect(mapState)(Pair) +export default connect(mapState)(Pair); diff --git a/sources/deeptree/src/Slice.jsx b/sources/deeptree/src/Slice.jsx index 5ee398d..d0b6ea6 100644 --- a/sources/deeptree/src/Slice.jsx +++ b/sources/deeptree/src/Slice.jsx @@ -1,48 +1,43 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; - +import React, { Component } from "react"; +import { connect } from "react-redux"; const mapStateToProps = (state, props) => { - return { - value: state[props.idx] - } -} + return { + value: state[props.idx] + }; +}; -const Counter = ({value}) => { - return
    Value: {value}
    -} +const Counter = ({ value }) => { + return
    Value: {value}
    ; +}; const ConnectedCounter = connect(mapStateToProps)(Counter); - class Slice extends Component { - state = {}; - - componentDidMount = () => { - //this.props.fillPairs(this.props.idx); + state = {}; + + componentDidMount = () => { + //this.props.fillPairs(this.props.idx); + }; + + render() { + const { remainingDepth, idx } = this.props; + + if (remainingDepth > 0) { + return ( +
    + {idx}.{remainingDepth} +
    + +
    +
    + ); } - render() { - const { remainingDepth, idx } = this.props; - - if(remainingDepth > 0) { - return ( -
    - {idx}.{remainingDepth} -
    - -
    -
    - ) - } - - return ( - - ); - } + return ; + } } Slice.displayName = "Slice"; - -export default Slice -//export default connect(mapStateToProps, actions)(Slice); \ No newline at end of file +export default Slice; +//export default connect(mapStateToProps, actions)(Slice); diff --git a/sources/deeptree/src/SpecialContext.js b/sources/deeptree/src/SpecialContext.js index cd47c43..5295b00 100644 --- a/sources/deeptree/src/SpecialContext.js +++ b/sources/deeptree/src/SpecialContext.js @@ -1,19 +1,19 @@ -import { createContext } from 'react' -const MoreEfficient = require('react-redux') +import { createContext } from "react"; +const MoreEfficient = require("react-redux"); -let context = { Provider: false, Consumer: false } +let context = { Provider: false, Consumer: false }; if (MoreEfficient.Context) { context = createContext(null, (prev, next) => { - let changes = 0 - const prevState = prev.state - const nextState = next.state + let changes = 0; + const prevState = prev.state; + const nextState = next.state; for (let i = 0; i < prevState.length; i++) { if (prevState[i] !== nextState[i]) { - changes |= (1 << (i%30)) + changes |= 1 << i % 30; } } - return changes - }) + return changes; + }); } -export default context \ No newline at end of file +export default context; diff --git a/sources/deeptree/src/configureStore.js b/sources/deeptree/src/configureStore.js index 6632ec2..5b0282f 100644 --- a/sources/deeptree/src/configureStore.js +++ b/sources/deeptree/src/configureStore.js @@ -1,12 +1,12 @@ -import {applyMiddleware, compose, createStore} from 'redux' -import { unstable_trace as trace } from 'scheduler/tracing'; +import { applyMiddleware, compose, createStore } from "redux"; +import { unstable_trace as trace } from "scheduler/tracing"; -import rootReducer from './counters'; -const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose +import rootReducer from "./counters"; +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { - if (typeof action === 'function') { + if (typeof action === "function") { return action(dispatch, getState, extraArgument); } @@ -14,25 +14,22 @@ function createThunkMiddleware(extraArgument) { }; } - - const reactProfilerMiddleware = store => next => action => { - const { type } = action; - return trace(`[redux] ${type}`, performance.now(), () => next(action)); -} + const { type } = action; + return trace(`[redux] ${type}`, performance.now(), () => next(action)); +}; const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; - export default function configureStore(preloadedState) { - const middlewares = [thunk] - const middlewareEnhancer = applyMiddleware(...middlewares) + const middlewares = [thunk]; + const middlewareEnhancer = applyMiddleware(...middlewares); - const enhancers = [middlewareEnhancer] - const composedEnhancers = composeEnhancers(...enhancers) + const enhancers = [middlewareEnhancer]; + const composedEnhancers = composeEnhancers(...enhancers); - const store = createStore(rootReducer, preloadedState, composedEnhancers) + const store = createStore(rootReducer, preloadedState, composedEnhancers); - return store -} \ No newline at end of file + return store; +} diff --git a/sources/deeptree/src/constants.js b/sources/deeptree/src/constants.js index 9c68e46..09e7cd3 100644 --- a/sources/deeptree/src/constants.js +++ b/sources/deeptree/src/constants.js @@ -1,7 +1,7 @@ -export const FILL_PAIRS = 'fill-pairs' -export const UPDATE_PAIR = 'update-pair' +export const FILL_PAIRS = "fill-pairs"; +export const UPDATE_PAIR = "update-pair"; export const NUMBER_OF_SLICES = 250; export const NUM_ENTRIES = 3500; -export const TREE_DEPTH = 15; \ No newline at end of file +export const TREE_DEPTH = 15; diff --git a/sources/deeptree/src/counters.js b/sources/deeptree/src/counters.js index 1a671f8..7b907e8 100644 --- a/sources/deeptree/src/counters.js +++ b/sources/deeptree/src/counters.js @@ -1,28 +1,35 @@ -import {createSlice} from "redux-starter-kit"; +import { createSlice } from "redux-starter-kit"; import * as c from "./constants"; -const {reducer, actions} = createSlice({ - initialState : {}, - reducers : { - initialize(state, action) { - const {numberOfCounters} = action.payload; - for(let i = 0; i < numberOfCounters; i++) { - state[i] = 0; - } - }, - increment(state, action) { - const {counterId} = action.payload; - const value = state[counterId] || 0; - state[counterId] = value + 1; +const { reducer, actions } = createSlice({ + initialState: {}, + reducers: { + initialize(state, action) { + const { numberOfCounters } = action.payload; + for (let i = 0; i < numberOfCounters; i++) { + state[i] = 0; + } + }, + increment(state, action) { + const { counterId } = action.payload; + }, + incrementMany(state, action) { + const { mod } = action.payload; + for (let counterId = 0; counterId < c.NUMBER_OF_SLICES; counterId++) { + if (counterId % mod === 0) { + const value = state[counterId] || 0; + state[counterId] = value + 1; } + } } + } }); -export const {initialize, increment} = actions; +export const { initialize, increment, incrementMany } = actions; export function incrementRandomCounter() { - const counterId = Math.floor(Math.random() * c.NUMBER_OF_SLICES); - return increment({counterId}) + const counterId = Math.floor(Math.random() * c.NUMBER_OF_SLICES); + return increment({ counterId }); } -export default reducer; \ No newline at end of file +export default reducer; diff --git a/sources/deeptree/src/index.js b/sources/deeptree/src/index.js index ad7d8ef..af2469d 100644 --- a/sources/deeptree/src/index.js +++ b/sources/deeptree/src/index.js @@ -1,60 +1,67 @@ -import React, {unstable_Profiler as Profiler} from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import 'fps-emit' +import React, { unstable_Profiler as Profiler } from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; +import "fps-emit"; -import * as c from './constants'; -//import { updatePair, updateRandomPairInSlice, fillPairs } from './pairActions'; -import {initialize, incrementRandomCounter} from "./counters"; +import * as c from "./constants"; +import { initialize, incrementRandomCounter, incrementMany } from "./counters"; -import {Provider} from "react-redux"; +import { Provider } from "react-redux"; import configureStore from "./configureStore"; const store = configureStore(); -store.dispatch(initialize({numberOfCounters: c.NUMBER_OF_SLICES})); +store.dispatch(initialize({ numberOfCounters: c.NUMBER_OF_SLICES })); const renderResults = []; window.renderResults = renderResults; - -function onAppRendered(id, phase, actualTime, baseTime, startTime, commitTime, interactions = []) { - if(!Array.isArray(interactions)) { - interactions = [...interactions] - } - renderResults.push({id, phase, actualTime, baseTime, startTime, commitTime, interactions}); +function onAppRendered( + id, + phase, + actualTime, + baseTime, + startTime, + commitTime, + interactions = [] +) { + if (!Array.isArray(interactions)) { + interactions = [...interactions]; + } + renderResults.push({ + id, + phase, + actualTime, + baseTime, + startTime, + commitTime, + interactions + }); } ReactDOM.render( - - - - - , - document.getElementById('root') + + + + + , + document.getElementById("root") ); -/* -function updateRandomPairInSlice() { - const sliceId = Math.floor(Math.random() * c.NUMBER_OF_SLICES); - const pairId = Math.floor(Math.random() * (c.NUM_ENTRIES / c.NUMBER_OF_SLICES)); - store.dispatch(updatePair(sliceId, pairId)); -} -*/ function doRandomUpdate() { store.dispatch(incrementRandomCounter()); } -//setInterval(updateRandomPairInSlice, 500); - - -setInterval(doRandomUpdate, 13) +function doUpdateMany(mod) { + store.dispatch(incrementMany({ mod })); +} -setInterval(doRandomUpdate, 21) +setInterval(doRandomUpdate, 13); -setInterval(doRandomUpdate, 34) +setInterval(() => doUpdateMany(5), 21); -setInterval(doRandomUpdate, 55) +setInterval(doRandomUpdate, 34); +setInterval(() => doUpdateMany(3), 55); diff --git a/sources/deeptree/src/pairActions.js b/sources/deeptree/src/pairActions.js index 986402d..c7eb8a4 100644 --- a/sources/deeptree/src/pairActions.js +++ b/sources/deeptree/src/pairActions.js @@ -1,47 +1,48 @@ - -import Chance from 'chance' -import * as c from './constants' +import Chance from "chance"; +import * as c from "./constants"; const chance = new Chance(); -function createPairs () { +function createPairs() { const pairs = []; const entries = Math.floor(c.NUM_ENTRIES / c.NUMBER_OF_SLICES); for (let i = 0; i < entries; i++) { - const pair = chance.currency_pair() + const pair = chance.currency_pair(); pairs.push({ id: i, value: Math.random(), name: pair[0].code + pair[1].code - }) + }); } - return pairs + return pairs; } -export function fillPairs (id) { +export function fillPairs(id) { return { type: `${c.FILL_PAIRS}_${id}`, pairs: createPairs() - } + }; } // function getRandIndex () { // return Math.floor(Math.random() * Math.floor(c.NUM_ENTRIES / c.NUMBER_OF_SLICES)) // } -export function updatePair (sliceId, pairId) { +export function updatePair(sliceId, pairId) { //console.log(sliceId, pairId); - return { - type: `${c.UPDATE_PAIR}_${sliceId}`, - id: pairId, - value: Math.random() - } + return { + type: `${c.UPDATE_PAIR}_${sliceId}`, + id: pairId, + value: Math.random() + }; } export function updateRandomPairInSlice() { return (dispatch, getState) => { - const pairId = Math.floor(Math.random() * (c.NUM_ENTRIES / c.NUMBER_OF_SLICES)); + const pairId = Math.floor( + Math.random() * (c.NUM_ENTRIES / c.NUMBER_OF_SLICES) + ); const sliceId = Math.floor(Math.random() * c.NUMBER_OF_SLICES); dispatch(updatePair(sliceId, pairId)); - } -} \ No newline at end of file + }; +} diff --git a/sources/deeptree/src/pairsReducer.js b/sources/deeptree/src/pairsReducer.js index faa95c1..aae5a79 100644 --- a/sources/deeptree/src/pairsReducer.js +++ b/sources/deeptree/src/pairsReducer.js @@ -1,30 +1,32 @@ -import { combineReducers } from 'redux'; -import * as c from './constants' +import { combineReducers } from "redux"; +import * as c from "./constants"; +const initialState = []; -const initialState = [] - -const highOrderSliceReducer = (sliceId = '') => (state = initialState, action) => { +const highOrderSliceReducer = (sliceId = "") => ( + state = initialState, + action +) => { switch (action.type) { case `${c.FILL_PAIRS}_${sliceId}`: { return [...action.pairs]; } case `${c.UPDATE_PAIR}_${sliceId}`: { return state.map(pair => { - return pair.id === action.id - ? {...pair, value: action.value } - : pair - }) + return pair.id === action.id ? { ...pair, value: action.value } : pair; + }); } default: { - return state + return state; } } -} +}; -const reducers = Array(c.NUMBER_OF_SLICES).fill(0).reduce((acc, curr, i) => { - acc[i] = highOrderSliceReducer(i); - return acc; -}, {}); +const reducers = Array(c.NUMBER_OF_SLICES) + .fill(0) + .reduce((acc, curr, i) => { + acc[i] = highOrderSliceReducer(i); + return acc; + }, {}); export default combineReducers(reducers); diff --git a/sources/deeptree/src/registerServiceWorker.js b/sources/deeptree/src/registerServiceWorker.js index a3e6c0c..a682a17 100644 --- a/sources/deeptree/src/registerServiceWorker.js +++ b/sources/deeptree/src/registerServiceWorker.js @@ -9,9 +9,9 @@ // This link also includes instructions on opting out of this behavior. const isLocalhost = Boolean( - window.location.hostname === 'localhost' || + window.location.hostname === "localhost" || // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || + window.location.hostname === "[::1]" || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname.match( /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ @@ -19,7 +19,7 @@ const isLocalhost = Boolean( ); export default function register() { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL(process.env.PUBLIC_URL, window.location); if (publicUrl.origin !== window.location.origin) { @@ -29,7 +29,7 @@ export default function register() { return; } - window.addEventListener('load', () => { + window.addEventListener("load", () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { @@ -40,8 +40,8 @@ export default function register() { // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://goo.gl/SC7cgQ' + "This web app is being served cache-first by a service " + + "worker. To learn more, visit https://goo.gl/SC7cgQ" ); }); } else { @@ -59,25 +59,25 @@ function registerValidSW(swUrl) { registration.onupdatefound = () => { const installingWorker = registration.installing; installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { + if (installingWorker.state === "installed") { if (navigator.serviceWorker.controller) { // At this point, the old content will have been purged and // the fresh content will have been added to the cache. // It's the perfect time to display a "New content is // available; please refresh." message in your web app. - console.log('New content is available; please refresh.'); + console.log("New content is available; please refresh."); } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + console.log("Content is cached for offline use."); } } }; }; }) .catch(error => { - console.error('Error during service worker registration:', error); + console.error("Error during service worker registration:", error); }); } @@ -88,7 +88,7 @@ function checkValidServiceWorker(swUrl) { // Ensure service worker exists, and that we really are getting a JS file. if ( response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 + response.headers.get("content-type").indexOf("javascript") === -1 ) { // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then(registration => { @@ -103,13 +103,13 @@ function checkValidServiceWorker(swUrl) { }) .catch(() => { console.log( - 'No internet connection found. App is running in offline mode.' + "No internet connection found. App is running in offline mode." ); }); } export function unregister() { - if ('serviceWorker' in navigator) { + if ("serviceWorker" in navigator) { navigator.serviceWorker.ready.then(registration => { registration.unregister(); }); diff --git a/sources/forms/.gitignore b/sources/forms/.gitignore new file mode 100644 index 0000000..4d29575 --- /dev/null +++ b/sources/forms/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/sources/forms/README.md b/sources/forms/README.md new file mode 100644 index 0000000..9d9614c --- /dev/null +++ b/sources/forms/README.md @@ -0,0 +1,68 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `npm start` + +Runs the app in the development mode.
    +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
    +You will also see any lint errors in the console. + +### `npm test` + +Launches the test runner in the interactive watch mode.
    +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `npm run build` + +Builds the app for production to the `build` folder.
    +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
    +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `npm run eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting + +### Analyzing the Bundle Size + +This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size + +### Making a Progressive Web App + +This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app + +### Advanced Configuration + +This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration + +### Deployment + +This section has moved here: https://facebook.github.io/create-react-app/docs/deployment + +### `npm run build` fails to minify + +This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify diff --git a/sources/forms/config-overrides.js b/sources/forms/config-overrides.js new file mode 100644 index 0000000..ae02366 --- /dev/null +++ b/sources/forms/config-overrides.js @@ -0,0 +1,15 @@ +module.exports = function override(config, env) { + //do stuff with the webpack config... + console.log(`Environment: ${env}`); + + if (env === "production") { + config.externals = { + react: "React", + redux: "Redux", + "react-dom": "ReactDOM", + "react-redux": "ReactRedux" + }; + } + + return config; +}; diff --git a/sources/forms/package.json b/sources/forms/package.json new file mode 100644 index 0000000..6fde0ab --- /dev/null +++ b/sources/forms/package.json @@ -0,0 +1,38 @@ +{ + "name": "forms", + "version": "0.1.0", + "private": true, + "installConfig": { + "pnp": true + }, + "dependencies": { + "chance": "^1.0.16", + "fps-emit": "file:../../fps-emit", + "react": "^16.8.2", + "react-dom": "^16.8.2", + "react-redux": "^6.0.0", + "react-scripts": "2.1.5", + "redux": "^4.0.0", + "redux-starter-kit": "^0.4.3", + "reselect": "^3.0.1", + "user-event": "^1.4.4" + }, + "scripts": { + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "test": "react-app-rewired --env=jsdom", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencies": { + "react-app-rewired": "^2.1.0" + } +} diff --git a/sources/forms/public/favicon.ico b/sources/forms/public/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/sources/forms/public/favicon.ico differ diff --git a/sources/forms/public/index.html b/sources/forms/public/index.html new file mode 100644 index 0000000..f4e01c0 --- /dev/null +++ b/sources/forms/public/index.html @@ -0,0 +1,48 @@ + + + + + + + + + + + React App + + + + + +
    + + + + + + + + + diff --git a/sources/forms/public/manifest.json b/sources/forms/public/manifest.json new file mode 100644 index 0000000..1f2f141 --- /dev/null +++ b/sources/forms/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/sources/forms/src/App.css b/sources/forms/src/App.css new file mode 100644 index 0000000..b41d297 --- /dev/null +++ b/sources/forms/src/App.css @@ -0,0 +1,33 @@ +.App { + text-align: center; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; + pointer-events: none; +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/sources/forms/src/App.js b/sources/forms/src/App.js new file mode 100644 index 0000000..5845f7b --- /dev/null +++ b/sources/forms/src/App.js @@ -0,0 +1,48 @@ +import React from "react"; +import { connect } from "react-redux"; + +import Form from "./Form"; +import * as c from "./constants"; + +import { typeTextInRandomInput } from "./inputs"; + +let slices; + +const mapState = state => { + if (!slices) { + slices = Object.keys(state).map(key => Number(key)); + //slices.sort(); + } + + return { slices }; +}; + +//const mapDispatch = { typeTextInRandomInput }; + +async function infiniteBobRoss() { + while (true) { + await typeTextInRandomInput(); + } +} + +class App extends React.Component { + render() { + return ( +
    + +
    + {this.props.slices.map((slice, idx) => { + return ( +
    +
    +
    + ); + })} +
    +
    + ); + } +} +App.displayName = "App"; + +export default connect(mapState)(App); diff --git a/sources/forms/src/App.test.js b/sources/forms/src/App.test.js new file mode 100644 index 0000000..23c181f --- /dev/null +++ b/sources/forms/src/App.test.js @@ -0,0 +1,9 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import App from "./App"; + +it("renders without crashing", () => { + const div = document.createElement("div"); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/sources/forms/src/Form.jsx b/sources/forms/src/Form.jsx new file mode 100644 index 0000000..98c0519 --- /dev/null +++ b/sources/forms/src/Form.jsx @@ -0,0 +1,43 @@ +import React from "react"; +import { connect } from "react-redux"; + +import { updateInput } from "./inputs"; + +import * as c from "./constants"; + +const mapState = (state, ownProps) => { + return { + text: state[ownProps.id] + }; +}; + +const mapDispatch = { updateInput }; + +class Form extends React.Component { + onChange = e => { + this.props.updateInput({ inputId: this.props.id, text: e.target.value }); + }; + + render() { + const { text, id } = this.props; + + const fillers = Array.from({ + length: c.NUMBER_OF_CHECKBOXES_PER_FORM + }).map((item, i) => ); + + return ( + + + Form {id}: +