From 1e151640a81496b8e5cca481fab7ad9ded91d46f Mon Sep 17 00:00:00 2001 From: Kevin Robinson Date: Fri, 3 Jul 2015 23:38:03 +0200 Subject: [PATCH 1/4] Loggit TodoMVC Broke all of Redux Broke all of Redux more At a good checkpoint, basic logging working Done with hack quality for now, halfway started GC Working, debugging compaction since markTodo isn't descriptive enough Done with tinkering In progress with HOC but stopping Revert "In progress with HOC but stopping" This reverts commit 672e5fe4c91f02798ee69fb126827e8ba713866b. Moving TodoItem state to log Trying to disable hot loading, tweaking some profiling Adding #start to interface for renderer, adding RafReactRenderer Initial progress on MemoizingOptimizer With memoizing snapshot optimizer, and initial analysis for test set 1 Some cleanup, adding more graphs comment cleanup Added PrecomputeReactRenderer, split out RenderTimer Tons of cleanup, profiling APIs, shell stitching pieces together Adding results for test2 Link to test2 results Rename to loggit-todomvc --- examples/{todomvc => loggit-todomvc}/.babelrc | 0 .../loggit-todomvc/actions/ActionTypes.js | 24 +++ .../loggit-todomvc/actions/TodoActions.js | 69 ++++++++ .../loggit-todomvc/components/Debugger.js | 96 +++++++++++ .../components/Footer.js | 0 .../components/Header.js | 13 +- .../components/MainSection.js | 50 ++++-- examples/loggit-todomvc/components/TodoApp.js | 21 +++ .../loggit-todomvc/components/TodoItem.js | 87 ++++++++++ .../components/TodoTextInput.js | 20 ++- .../constants/TodoFilters.js | 0 .../{todomvc => loggit-todomvc}/index.html | 2 +- examples/loggit-todomvc/index.js | 17 ++ examples/loggit-todomvc/loggit/README.md | 115 +++++++++++++ examples/loggit-todomvc/loggit/compactor.js | 15 ++ examples/loggit-todomvc/loggit/log.js | 52 ++++++ .../loggit/optimizers/memoizing_optimizer.js | 81 +++++++++ .../optimizers/memoizing_optimizer_v2.js | 77 +++++++++ .../memoizing_snapshot_optimizer.js | 143 ++++++++++++++++ .../loggit/optimizers/noop_optimizer.js | 33 ++++ .../loggit/profiling_reporter.js | 31 ++++ .../loggit/react_interpreter.js | 29 ++++ .../loggit/renderers/naive_react_renderer.js | 44 +++++ .../renderers/precompute_react_renderer.js | 157 ++++++++++++++++++ .../loggit/renderers/raf_react_renderer.js | 58 +++++++ examples/loggit-todomvc/loggit/shell.js | 138 +++++++++++++++ examples/loggit-todomvc/loggit/timer.js | 28 ++++ .../{todomvc => loggit-todomvc}/package.json | 14 +- .../{todomvc => loggit-todomvc}/server.js | 2 +- .../loggit-todomvc/stores/compaction_fn.js | 45 +++++ .../loggit-todomvc/stores/is_editing_map.js | 22 +++ examples/loggit-todomvc/stores/todos.js | 76 +++++++++ .../test/cpu-profile-test1-optimizer3.png | Bin 0 -> 504002 bytes .../loggit-todomvc/test/initial_facts_set1.js | 1 + .../test/profile-test1-optimizer3.png | Bin 0 -> 175738 bytes .../loggit-todomvc/test/test1-results.png | Bin 0 -> 81405 bytes .../loggit-todomvc/test/test1_results.csv | 28 ++++ .../loggit-todomvc/test/test1_results.numbers | Bin 0 -> 129235 bytes .../loggit-todomvc/test/test2_results.csv | 35 ++++ .../webpack.config.js | 7 +- examples/todomvc/actions/TodoActions.js | 42 ----- examples/todomvc/components/TodoItem.js | 68 -------- examples/todomvc/constants/ActionTypes.js | 6 - examples/todomvc/containers/App.js | 17 -- examples/todomvc/containers/TodoApp.js | 26 --- examples/todomvc/index.js | 8 - examples/todomvc/stores/index.js | 1 - examples/todomvc/stores/todos.js | 50 ------ 48 files changed, 1585 insertions(+), 263 deletions(-) rename examples/{todomvc => loggit-todomvc}/.babelrc (100%) create mode 100644 examples/loggit-todomvc/actions/ActionTypes.js create mode 100644 examples/loggit-todomvc/actions/TodoActions.js create mode 100644 examples/loggit-todomvc/components/Debugger.js rename examples/{todomvc => loggit-todomvc}/components/Footer.js (100%) rename examples/{todomvc => loggit-todomvc}/components/Header.js (51%) rename examples/{todomvc => loggit-todomvc}/components/MainSection.js (58%) create mode 100644 examples/loggit-todomvc/components/TodoApp.js create mode 100644 examples/loggit-todomvc/components/TodoItem.js rename examples/{todomvc => loggit-todomvc}/components/TodoTextInput.js (76%) rename examples/{todomvc => loggit-todomvc}/constants/TodoFilters.js (100%) rename examples/{todomvc => loggit-todomvc}/index.html (80%) create mode 100644 examples/loggit-todomvc/index.js create mode 100644 examples/loggit-todomvc/loggit/README.md create mode 100644 examples/loggit-todomvc/loggit/compactor.js create mode 100644 examples/loggit-todomvc/loggit/log.js create mode 100644 examples/loggit-todomvc/loggit/optimizers/memoizing_optimizer.js create mode 100644 examples/loggit-todomvc/loggit/optimizers/memoizing_optimizer_v2.js create mode 100644 examples/loggit-todomvc/loggit/optimizers/memoizing_snapshot_optimizer.js create mode 100644 examples/loggit-todomvc/loggit/optimizers/noop_optimizer.js create mode 100644 examples/loggit-todomvc/loggit/profiling_reporter.js create mode 100644 examples/loggit-todomvc/loggit/react_interpreter.js create mode 100644 examples/loggit-todomvc/loggit/renderers/naive_react_renderer.js create mode 100644 examples/loggit-todomvc/loggit/renderers/precompute_react_renderer.js create mode 100644 examples/loggit-todomvc/loggit/renderers/raf_react_renderer.js create mode 100644 examples/loggit-todomvc/loggit/shell.js create mode 100644 examples/loggit-todomvc/loggit/timer.js rename examples/{todomvc => loggit-todomvc}/package.json (67%) rename examples/{todomvc => loggit-todomvc}/server.js (96%) create mode 100644 examples/loggit-todomvc/stores/compaction_fn.js create mode 100644 examples/loggit-todomvc/stores/is_editing_map.js create mode 100644 examples/loggit-todomvc/stores/todos.js create mode 100644 examples/loggit-todomvc/test/cpu-profile-test1-optimizer3.png create mode 100644 examples/loggit-todomvc/test/initial_facts_set1.js create mode 100644 examples/loggit-todomvc/test/profile-test1-optimizer3.png create mode 100644 examples/loggit-todomvc/test/test1-results.png create mode 100644 examples/loggit-todomvc/test/test1_results.csv create mode 100644 examples/loggit-todomvc/test/test1_results.numbers create mode 100644 examples/loggit-todomvc/test/test2_results.csv rename examples/{todomvc => loggit-todomvc}/webpack.config.js (78%) delete mode 100644 examples/todomvc/actions/TodoActions.js delete mode 100644 examples/todomvc/components/TodoItem.js delete mode 100644 examples/todomvc/constants/ActionTypes.js delete mode 100644 examples/todomvc/containers/App.js delete mode 100644 examples/todomvc/containers/TodoApp.js delete mode 100644 examples/todomvc/index.js delete mode 100644 examples/todomvc/stores/index.js delete mode 100644 examples/todomvc/stores/todos.js diff --git a/examples/todomvc/.babelrc b/examples/loggit-todomvc/.babelrc similarity index 100% rename from examples/todomvc/.babelrc rename to examples/loggit-todomvc/.babelrc diff --git a/examples/loggit-todomvc/actions/ActionTypes.js b/examples/loggit-todomvc/actions/ActionTypes.js new file mode 100644 index 0000000000..3d32d4a5bf --- /dev/null +++ b/examples/loggit-todomvc/actions/ActionTypes.js @@ -0,0 +1,24 @@ +import _ from 'lodash'; + +// Hides actual keys so everyone uses these constants. +function mirrorKeys(keys) { + return keys.reduce((actionMap, key) => { + return { + [key]: _.uniqueId(key + ':'), + ...actionMap + }; + }, {}); +} + +export default mirrorKeys([ + 'ADDED_TODO', + 'DELETED_TODO', + 'EDITED_TODO', + 'CHECK_TODO', + 'UNCHECK_TODO', + 'CHECK_ALL', + 'UNCHECK_ALL', + 'CLEAR_MARKED', + 'FINISHED_EDITING_TODO', + 'WILL_EDIT_TODO' +]); diff --git a/examples/loggit-todomvc/actions/TodoActions.js b/examples/loggit-todomvc/actions/TodoActions.js new file mode 100644 index 0000000000..85a869086e --- /dev/null +++ b/examples/loggit-todomvc/actions/TodoActions.js @@ -0,0 +1,69 @@ +import * as types from './ActionTypes'; + +export function addTodo(text) { + return { + type: types.ADDED_TODO, + text + }; +} + +export function deleteTodo(id) { + return { + type: types.DELETED_TODO, + id + }; +} + +export function editTodo(id, text) { + return { + type: types.EDITED_TODO, + id, + text + }; +} + +export function checkTodo(id) { + return { + type: types.CHECK_TODO, + id + }; +} + +export function uncheckTodo(id) { + return { + type: types.UNCHECK_TODO, + id + }; +} + +export function checkAll() { + return { + type: types.CHECK_ALL + }; +} + +export function uncheckAll() { + return { + type: types.UNCHECK_ALL + }; +} + +export function clearMarked() { + return { + type: types.CLEAR_MARKED + }; +} + +export function willEditTodo(todoId) { + return { + type: types.WILL_EDIT_TODO, + todoId: todoId + }; +} + +export function finishedEditingTodo(todoId) { + return { + type: types.FINISHED_EDITING_TODO, + todoId: todoId + }; +} diff --git a/examples/loggit-todomvc/components/Debugger.js b/examples/loggit-todomvc/components/Debugger.js new file mode 100644 index 0000000000..4595c71039 --- /dev/null +++ b/examples/loggit-todomvc/components/Debugger.js @@ -0,0 +1,96 @@ +import React from 'react'; +import * as TodoActions from '../actions/TodoActions'; +import compactionKey from '../stores/compaction_fn'; + +// For hacking on internals +export default class Debugger extends React.Component { + static propTypes = { + loggit: React.PropTypes.object.isRequired + }; + + constructor(props, context) { + super(props, context); + this.state = { isMonkeyAwake: false }; + this.MonkeyTimer = null; + this.pokeMonkey = this.pokeMonkey.bind(this); + } + + // For easier profiling + componentDidMount() { + this.setState({ isMonkeyAwake: true }); + const before = this.profileSnapshot(); + window.setTimeout(() => { + this.setState({ isMonkeyAwake: false }); + const after = this.profileSnapshot(); + this.outputProfiling(before, after); + }, 3000); + } + + profileSnapshot() { + return { + heap: window.performance.memory.usedJSHeapSize + }; + } + + outputProfiling(before, after) { + // console.table([ + // { heap: before.heap }, + // { heap: after.heap }, + // { heap: '+' + (after.heap - before.heap) } + // ]); + console.info({ heap: after.heap, delta: '+' + (after.heap - before.heap) }); + console.info(window.profilingReporter.printStats()); + } + + handleMonkey() { + this.setState({ isMonkeyAwake: !this.state.isMonkeyAwake }); + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.isMonkeyAwake === this.state.isMonkeyAwake) { + return; + } + + if (this.state.isMonkeyAwake) { + this.MonkeyTimer = window.setInterval(this.pokeMonkey, 10); + } else { + window.clearInterval(this.MonkeyTimer); + } + } + + pokeMonkey() { + const actionFns = [ + TodoActions.checkAll, + TodoActions.uncheckAll, + TodoActions.clearMarked, + () => TodoActions.addTodo('do something: ' + Math.random()) + ] + const randomIndex = Math.floor(Math.random() * actionFns.length); + const randomAction = actionFns[randomIndex](); + this.props.loggit.recordFact(randomAction); + } + + forceCompaction() { + this.props.loggit.experimental.forceCompaction(); + } + + render() { + return ( +
+ + +
+ ); + } +} diff --git a/examples/todomvc/components/Footer.js b/examples/loggit-todomvc/components/Footer.js similarity index 100% rename from examples/todomvc/components/Footer.js rename to examples/loggit-todomvc/components/Footer.js diff --git a/examples/todomvc/components/Header.js b/examples/loggit-todomvc/components/Header.js similarity index 51% rename from examples/todomvc/components/Header.js rename to examples/loggit-todomvc/components/Header.js index 85e143b0d5..cfc8787055 100644 --- a/examples/todomvc/components/Header.js +++ b/examples/loggit-todomvc/components/Header.js @@ -1,23 +1,24 @@ import React, { PropTypes } from 'react'; import TodoTextInput from './TodoTextInput'; +import * as TodoActions from '../actions/TodoActions'; export default class Header { static propTypes = { - addTodo: PropTypes.func.isRequired + loggit: PropTypes.object.isRequired }; handleSave(text) { - if (text.length !== 0) { - this.props.addTodo(text); - } + if (text.length === 0) return; + const userAddedTodo = TodoActions.addTodo(text); + this.props.loggit.recordFact(userAddedTodo); } render() { return (

todos

-
); diff --git a/examples/todomvc/components/MainSection.js b/examples/loggit-todomvc/components/MainSection.js similarity index 58% rename from examples/todomvc/components/MainSection.js rename to examples/loggit-todomvc/components/MainSection.js index 42ee18561f..7e3ee369c5 100644 --- a/examples/todomvc/components/MainSection.js +++ b/examples/loggit-todomvc/components/MainSection.js @@ -2,6 +2,9 @@ import React, { Component, PropTypes } from 'react'; import TodoItem from './TodoItem'; import Footer from './Footer'; import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters'; +import ComputeTodos from '../stores/todos.js' +import * as TodoActions from '../actions/TodoActions'; + const TODO_FILTERS = { [SHOW_ALL]: () => true, @@ -11,8 +14,7 @@ const TODO_FILTERS = { export default class MainSection extends Component { static propTypes = { - todos: PropTypes.array.isRequired, - actions: PropTypes.object.isRequired + loggit: PropTypes.object.isRequired }; constructor(props, context) { @@ -21,18 +23,37 @@ export default class MainSection extends Component { } handleClearMarked() { - const atLeastOneMarked = this.props.todos.some(todo => todo.marked); - if (atLeastOneMarked) { - this.props.actions.clearMarked(); - } + const {todos} = this.data(); + const atLeastOneMarked = todos.some(todo => todo.marked); + if (!atLeastOneMarked) return; + + this.props.loggit.recordFact(TodoActions.clearMarked()); } handleShow(filter) { this.setState({ filter }); } + handleInputChanged() { + const {todos} = this.data(); + const fact = (todos.every(todo => todo.marked)) + ? TodoActions.uncheckAll() + : TodoActions.checkAll(); + this.props.loggit.recordFact(fact); + } + + computations() { + return { + todos: ComputeTodos + } + } + + data() { + return this.props.loggit.computeFor(this); + } + render() { - const { todos, actions } = this.props; + const { todos } = this.data(); const { filter } = this.state; const filteredTodos = todos.filter(TODO_FILTERS[filter]); @@ -43,10 +64,10 @@ export default class MainSection extends Component { return (
- {this.renderToggleAll(markedCount)} + {this.renderToggleAll(todos, markedCount)} {this.renderFooter(markedCount)} @@ -54,20 +75,19 @@ export default class MainSection extends Component { ); } - renderToggleAll(markedCount) { - const { todos, actions } = this.props; + renderToggleAll(todos, markedCount) { if (todos.length > 0) { return ( + onChange={this.handleInputChanged.bind(this)} /> ); } } renderFooter(markedCount) { - const { todos } = this.props; + const { todos } = this.data(); const { filter } = this.state; const unmarkedCount = todos.length - markedCount; @@ -76,8 +96,8 @@ export default class MainSection extends Component {