From e678873a97d30044be99a8a8d2a33b467f16df7f Mon Sep 17 00:00:00 2001 From: Andrey Konstantinov Date: Thu, 13 Feb 2020 09:45:51 +1300 Subject: [PATCH 1/3] added react-hookstate and preact-hookstate frameworks --- frameworks/keyed/preact-hookstate/.babelrc | 11 ++ frameworks/keyed/preact-hookstate/index.html | 12 ++ .../keyed/preact-hookstate/package.json | 37 +++++ .../keyed/preact-hookstate/src/Main.jsx | 144 ++++++++++++++++++ .../keyed/preact-hookstate/webpack.config.js | 58 +++++++ frameworks/keyed/react-hookstate/index.html | 12 ++ frameworks/keyed/react-hookstate/package.json | 39 +++++ frameworks/keyed/react-hookstate/src/main.jsx | 137 +++++++++++++++++ .../keyed/react-hookstate/webpack.config.js | 78 ++++++++++ 9 files changed, 528 insertions(+) create mode 100644 frameworks/keyed/preact-hookstate/.babelrc create mode 100644 frameworks/keyed/preact-hookstate/index.html create mode 100644 frameworks/keyed/preact-hookstate/package.json create mode 100644 frameworks/keyed/preact-hookstate/src/Main.jsx create mode 100644 frameworks/keyed/preact-hookstate/webpack.config.js create mode 100644 frameworks/keyed/react-hookstate/index.html create mode 100644 frameworks/keyed/react-hookstate/package.json create mode 100644 frameworks/keyed/react-hookstate/src/main.jsx create mode 100644 frameworks/keyed/react-hookstate/webpack.config.js diff --git a/frameworks/keyed/preact-hookstate/.babelrc b/frameworks/keyed/preact-hookstate/.babelrc new file mode 100644 index 000000000..e25d3113f --- /dev/null +++ b/frameworks/keyed/preact-hookstate/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": [ + ["@babel/preset-env", { + "targets": { + "browsers": ["last 1 chrome versions"] + } + }], + "@babel/preset-react" + ], + "plugins": [] +} \ No newline at end of file diff --git a/frameworks/keyed/preact-hookstate/index.html b/frameworks/keyed/preact-hookstate/index.html new file mode 100644 index 000000000..89932174e --- /dev/null +++ b/frameworks/keyed/preact-hookstate/index.html @@ -0,0 +1,12 @@ + + + + + Preact Hookstate + + + +
+ + + diff --git a/frameworks/keyed/preact-hookstate/package.json b/frameworks/keyed/preact-hookstate/package.json new file mode 100644 index 000000000..ceb5c51a6 --- /dev/null +++ b/frameworks/keyed/preact-hookstate/package.json @@ -0,0 +1,37 @@ +{ + "name": "js-framework-benchmark-react-lite", + "version": "1.0.0", + "description": "Benchmark for Preact+Hookstate framework", + "js-framework-benchmark": { + "frameworkVersionFromPackage": "preact:@hookstate/core" + }, + "scripts": { + "build-dev": "webpack -w -d", + "build-prod": "webpack" + }, + "keywords": [ + "ractive" + ], + "author": "Stefan Krause", + "license": "Apache-2.0", + "homepage": "https://github.com/krausest/js-framework-benchmark", + "repository": { + "type": "git", + "url": "https://github.com/krausest/js-framework-benchmark.git" + }, + "devDependencies": { + "babel-minify-webpack-plugin": "^0.3.1", + "@babel/core": "7.6.4", + "@babel/preset-env": "7.6.3", + "@babel/preset-react": "7.6.3", + "@babel/plugin-proposal-class-properties": "7.5.5", + "babel-loader": "8.0.6", + "webpack": "4.41.2", + "webpack-cli": "3.3.9" + }, + "dependencies": { + "@babel/plugin-transform-react-jsx": "7.3.0", + "@hookstate/core": "1.5.2", + "preact": "10.0.1" + } +} diff --git a/frameworks/keyed/preact-hookstate/src/Main.jsx b/frameworks/keyed/preact-hookstate/src/Main.jsx new file mode 100644 index 000000000..25f63ffb1 --- /dev/null +++ b/frameworks/keyed/preact-hookstate/src/Main.jsx @@ -0,0 +1,144 @@ +'use strict'; +/** @jsx preact.h */ + +var preact = require('preact'); +var React = require('react'); +var hookstate = require('@hookstate/core'); + +var { render } = preact; +var { useStateLink, createStateLink, None, Downgraded } = hookstate; + + +function random(max) { return Math.round(Math.random() * 1000) % max; } + +const A = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean", + "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", + "cheap", "expensive", "fancy"]; +const C = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"]; +const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", + "keyboard"]; + +let nextId = 1; + +function buildData(count) { + const data = {}; + for (let i = 0; i < count; i++) { + data[nextId] = { + id: nextId, + label: `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`, + }; + nextId += 1; + } + return data; +} + +const globalState = createStateLink({}); + +const GlyphIcon = ; + +const Row = ({ itemState, selectedRef }) => { + const state = useStateLink(itemState).with(Downgraded); + const item = state.value; + const select = () => { + if (selectedRef.current && selectedRef.current.value) { + selectedRef.current.set(p => { + p.selected = false; + return p; + }) + } + selectedRef.current = state + + state.set(p => { + p.selected = true; + return p; + }) + }; + const remove = () => state.set(None); + + return ( + {item.id} + {item.label} + {GlyphIcon} + + ); +} + +const Button = ({ id, cb, title }) => ( +
+ +
+); + +const Jumbotron = () => { + const dataState = globalState + + return (
+
+
+

Preact Hookstate keyed

+
+
+
+
+
+
+
) +} + +const Rows = () => { + const dataState = useStateLink(globalState); + const selectedRef = React.useRef(); + + return ( + {dataState.keys.map(itemKey => { + const itemState = dataState.nested[itemKey]; + return + })} +
) +} + +const Main = () => { + return (
+ + + +
); +} + +render(
, document.getElementById('main')); \ No newline at end of file diff --git a/frameworks/keyed/preact-hookstate/webpack.config.js b/frameworks/keyed/preact-hookstate/webpack.config.js new file mode 100644 index 000000000..424134263 --- /dev/null +++ b/frameworks/keyed/preact-hookstate/webpack.config.js @@ -0,0 +1,58 @@ +'use strict'; +var path = require('path') +const MinifyPlugin = require("babel-minify-webpack-plugin"); +var webpack = require('webpack') + +var cache = {}; +var loaders = [ + { + test: /\.jsx$/, + loader: 'babel-loader' + }, + { + test: /\.es6\.js$/, + loader: 'babel-loader' + }, + { + test: /\.css$/, + loader: 'style-loader!css-loader' + } +]; +var extensions = [ + '.js', '.jsx', '.es6.js', '.msx' +]; + +module.exports = [{ + cache: cache, + module: { + rules: loaders + }, + entry: { + main: './src/Main.jsx', + }, + output: { + path: path.resolve(__dirname, "dist"), + filename: '[name].js', + sourceMapFilename: "[file].map", + }, + resolve: { + extensions: extensions, + modules: [ + __dirname, + path.resolve(__dirname, "src"), + "node_modules" + ], + alias: { + "react": "node_modules/preact/compat/dist/compat.js", + "react-dom": "node_modules/preact/compat/dist/compat.js", + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify('production') + } + }), + new MinifyPlugin() + ] +}]; \ No newline at end of file diff --git a/frameworks/keyed/react-hookstate/index.html b/frameworks/keyed/react-hookstate/index.html new file mode 100644 index 000000000..d200761ca --- /dev/null +++ b/frameworks/keyed/react-hookstate/index.html @@ -0,0 +1,12 @@ + + + + + React Hookstate + + + +
+ + + diff --git a/frameworks/keyed/react-hookstate/package.json b/frameworks/keyed/react-hookstate/package.json new file mode 100644 index 000000000..93b4ccacd --- /dev/null +++ b/frameworks/keyed/react-hookstate/package.json @@ -0,0 +1,39 @@ +{ + "name": "js-framework-benchmark-react-hooks", + "version": "1.1.1", + "description": "React Hooks demo", + "main": "index.js", + "js-framework-benchmark": { + "frameworkVersionFromPackage": "react:@hookstate/core" + }, + "scripts": { + "build-dev": "webpack --watch", + "build-prod": "webpack" + }, + "keywords": [ + "react", + "webpack" + ], + "author": "Stefan Krause", + "license": "Apache-2.0", + "homepage": "https://github.com/krausest/js-framework-benchmark", + "repository": { + "type": "git", + "url": "https://github.com/krausest/js-framework-benchmark.git" + }, + "devDependencies": { + "@babel/core": "7.4.5", + "@babel/preset-env": "7.4.5", + "@babel/preset-react": "7.0.0", + "@babel/plugin-proposal-class-properties": "7.4.4", + "babel-loader": "8.0.6", + "terser-webpack-plugin": "1.3.0", + "webpack": "4.34.0", + "webpack-cli": "3.3.4" + }, + "dependencies": { + "@hookstate/core": "^1.5.2", + "react": "16.8.6", + "react-dom": "16.8.6" + } +} diff --git a/frameworks/keyed/react-hookstate/src/main.jsx b/frameworks/keyed/react-hookstate/src/main.jsx new file mode 100644 index 000000000..51829c486 --- /dev/null +++ b/frameworks/keyed/react-hookstate/src/main.jsx @@ -0,0 +1,137 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { useStateLink, createStateLink, None, Downgraded } from '@hookstate/core'; + +function random(max) { return Math.round(Math.random() * 1000) % max; } + +const A = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean", + "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", + "cheap", "expensive", "fancy"]; +const C = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"]; +const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", + "keyboard"]; + +let nextId = 1; + +function buildData(count) { + const data = {}; + for (let i = 0; i < count; i++) { + data[nextId] = { + id: nextId, + label: `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`, + }; + nextId += 1; + } + return data; +} + +const globalState = createStateLink({}); + +const GlyphIcon = ; + +const Row = ({ itemState, selectedRef }) => { + const state = useStateLink(itemState).with(Downgraded); + const item = state.value; + const select = () => { + if (selectedRef.current && selectedRef.current.value) { + selectedRef.current.set(p => { + p.selected = false; + return p; + }) + } + selectedRef.current = state + + state.set(p => { + p.selected = true; + return p; + }) + }; + const remove = () => state.set(None); + + return ( + {item.id} + {item.label} + {GlyphIcon} + + ); +} + +const Button = ({ id, cb, title }) => ( +
+ +
+); + +const Jumbotron = () => { + const dataState = globalState + + return (
+
+
+

React Hookstate keyed

+
+
+
+
+
+
+
) +} + +const Rows = () => { + const dataState = useStateLink(globalState); + const selectedRef = React.useRef(); + + return ( + {dataState.keys.map(itemKey => { + const itemState = dataState.nested[itemKey]; + return + })} +
) +} + +const Main = () => { + return (
+ + + +
); +} + +ReactDOM.render(
, document.getElementById('main')); diff --git a/frameworks/keyed/react-hookstate/webpack.config.js b/frameworks/keyed/react-hookstate/webpack.config.js new file mode 100644 index 000000000..f6607fcc0 --- /dev/null +++ b/frameworks/keyed/react-hookstate/webpack.config.js @@ -0,0 +1,78 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); + +module.exports = { + mode: 'production', + // mode: 'development', + entry: { + main: path.join(__dirname, 'src', 'main.jsx'), + }, + output: { + path: path.join(__dirname, 'dist'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.jsx'] + }, + module: { + rules: [{ + test: /\.jsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env', '@babel/preset-react'], + plugins: ['@babel/plugin-proposal-class-properties'], + } + } + ] + }] + }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + parse: { + // we want terser to parse ecma 8 code. However, we don't want it + // to apply any minfication steps that turns valid ecma 5 code + // into invalid ecma 5 code. This is why the 'compress' and 'output' + // sections only apply transformations that are ecma 5 safe + // https://github.com/facebook/create-react-app/pull/4234 + ecma: 8, + }, + compress: { + ecma: 5, + warnings: false, + // Disabled because of an issue with Uglify breaking seemingly valid code: + // https://github.com/facebook/create-react-app/issues/2376 + // Pending further investigation: + // https://github.com/mishoo/UglifyJS2/issues/2011 + comparisons: false, + }, + mangle: { + safari10: true, + }, + output: { + ecma: 5, + comments: false, + // Turned on because emoji and regex is not minified properly using default + // https://github.com/facebook/create-react-app/issues/2488 + ascii_only: true, + }, + }, + // Use multi-process parallel running to improve the build speed + // Default number of concurrent runs: os.cpus().length - 1 + parallel: true, + // Enable file caching + cache: true, + }), + ] + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { NODE_ENV: JSON.stringify('production') } + }), + ], +}; From 4b65ed97e5e86ba03cf9bcf1835c05ab61245b60 Mon Sep 17 00:00:00 2001 From: Andrey Konstantinov Date: Thu, 13 Feb 2020 12:12:21 +1300 Subject: [PATCH 2/3] format spaces to make it equal to preact --- frameworks/keyed/react-hookstate/src/main.jsx | 200 +++++++++--------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/frameworks/keyed/react-hookstate/src/main.jsx b/frameworks/keyed/react-hookstate/src/main.jsx index 51829c486..2c15eea94 100644 --- a/frameworks/keyed/react-hookstate/src/main.jsx +++ b/frameworks/keyed/react-hookstate/src/main.jsx @@ -5,24 +5,24 @@ import { useStateLink, createStateLink, None, Downgraded } from '@hookstate/core function random(max) { return Math.round(Math.random() * 1000) % max; } const A = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean", - "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", - "cheap", "expensive", "fancy"]; + "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", + "cheap", "expensive", "fancy"]; const C = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"]; const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", - "keyboard"]; + "keyboard"]; let nextId = 1; function buildData(count) { - const data = {}; - for (let i = 0; i < count; i++) { - data[nextId] = { - id: nextId, - label: `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`, - }; - nextId += 1; - } - return data; + const data = {}; + for (let i = 0; i < count; i++) { + data[nextId] = { + id: nextId, + label: `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`, + }; + nextId += 1; + } + return data; } const globalState = createStateLink({}); @@ -30,108 +30,108 @@ const globalState = createStateLink({}); const GlyphIcon = ; const Row = ({ itemState, selectedRef }) => { - const state = useStateLink(itemState).with(Downgraded); - const item = state.value; - const select = () => { - if (selectedRef.current && selectedRef.current.value) { - selectedRef.current.set(p => { - p.selected = false; - return p; - }) - } - selectedRef.current = state - - state.set(p => { - p.selected = true; - return p; - }) - }; - const remove = () => state.set(None); - - return ( - {item.id} - {item.label} - {GlyphIcon} - - ); + const state = useStateLink(itemState).with(Downgraded); + const item = state.value; + const select = () => { + if (selectedRef.current && selectedRef.current.value) { + selectedRef.current.set(p => { + p.selected = false; + return p; + }) + } + selectedRef.current = state + + state.set(p => { + p.selected = true; + return p; + }) + }; + const remove = () => state.set(None); + + return ( + {item.id} + {item.label} + {GlyphIcon} + + ); } const Button = ({ id, cb, title }) => ( -
- -
+
+ +
); const Jumbotron = () => { - const dataState = globalState - - return (
-
-
-

React Hookstate keyed

-
-
+ const dataState = globalState + + return (
-
+
-
-
- ) + ) } const Rows = () => { - const dataState = useStateLink(globalState); - const selectedRef = React.useRef(); - - return ( - {dataState.keys.map(itemKey => { - const itemState = dataState.nested[itemKey]; - return - })} -
) + const dataState = useStateLink(globalState); + const selectedRef = React.useRef(); + + return ( + {dataState.keys.map(itemKey => { + const itemState = dataState.nested[itemKey]; + return + })} +
) } const Main = () => { - return (
- - - -
); + return (
+ + + +
); } ReactDOM.render(
, document.getElementById('main')); From 9556df479737e207881d99ca54dc493056257c0c Mon Sep 17 00:00:00 2001 From: Andrey Konstantinov Date: Thu, 13 Feb 2020 15:02:39 +1300 Subject: [PATCH 3/3] fixed isKeyed check for hookstate frameworks, added non-keyed variant for hookstate --- .../keyed/preact-hookstate/src/Main.jsx | 28 ++-- frameworks/keyed/react-hookstate/src/main.jsx | 28 ++-- .../non-keyed/react-hookstate/index.html | 12 ++ .../non-keyed/react-hookstate/package.json | 39 ++++++ .../non-keyed/react-hookstate/src/main.jsx | 129 ++++++++++++++++++ .../react-hookstate/webpack.config.js | 78 +++++++++++ 6 files changed, 278 insertions(+), 36 deletions(-) create mode 100644 frameworks/non-keyed/react-hookstate/index.html create mode 100644 frameworks/non-keyed/react-hookstate/package.json create mode 100644 frameworks/non-keyed/react-hookstate/src/main.jsx create mode 100644 frameworks/non-keyed/react-hookstate/webpack.config.js diff --git a/frameworks/keyed/preact-hookstate/src/Main.jsx b/frameworks/keyed/preact-hookstate/src/Main.jsx index 25f63ffb1..b0fa0d916 100644 --- a/frameworks/keyed/preact-hookstate/src/Main.jsx +++ b/frameworks/keyed/preact-hookstate/src/Main.jsx @@ -33,29 +33,23 @@ function buildData(count) { } const globalState = createStateLink({}); +let selectedState = undefined; const GlyphIcon = ; -const Row = ({ itemState, selectedRef }) => { +const Row = ({ itemState }) => { const state = useStateLink(itemState).with(Downgraded); const item = state.value; const select = () => { - if (selectedRef.current && selectedRef.current.value) { - selectedRef.current.set(p => { - p.selected = false; - return p; - }) + if (selectedState && selectedState.nested) { + selectedState.nested.selected.set(false) } - selectedRef.current = state - - state.set(p => { - p.selected = true; - return p; - }) + selectedState = state + selectedState.nested.selected.set(true) }; const remove = () => state.set(None); - return ( + return ( {item.id} {item.label} {GlyphIcon} @@ -123,13 +117,11 @@ const Jumbotron = () => { const Rows = () => { const dataState = useStateLink(globalState); - const selectedRef = React.useRef(); return ( - {dataState.keys.map(itemKey => { - const itemState = dataState.nested[itemKey]; - return - })} + {dataState.keys.map(itemKey => + + )}
) } diff --git a/frameworks/keyed/react-hookstate/src/main.jsx b/frameworks/keyed/react-hookstate/src/main.jsx index 2c15eea94..ba257fcdb 100644 --- a/frameworks/keyed/react-hookstate/src/main.jsx +++ b/frameworks/keyed/react-hookstate/src/main.jsx @@ -26,29 +26,23 @@ function buildData(count) { } const globalState = createStateLink({}); +let selectedState = undefined; const GlyphIcon = ; -const Row = ({ itemState, selectedRef }) => { +const Row = ({ itemState }) => { const state = useStateLink(itemState).with(Downgraded); const item = state.value; const select = () => { - if (selectedRef.current && selectedRef.current.value) { - selectedRef.current.set(p => { - p.selected = false; - return p; - }) + if (selectedState && selectedState.nested) { + selectedState.nested.selected.set(false) } - selectedRef.current = state - - state.set(p => { - p.selected = true; - return p; - }) + selectedState = state + selectedState.nested.selected.set(true) }; const remove = () => state.set(None); - return ( + return ( {item.id} {item.label} {GlyphIcon} @@ -116,13 +110,11 @@ const Jumbotron = () => { const Rows = () => { const dataState = useStateLink(globalState); - const selectedRef = React.useRef(); return ( - {dataState.keys.map(itemKey => { - const itemState = dataState.nested[itemKey]; - return - })} + {dataState.keys.map(itemKey => + + )}
) } diff --git a/frameworks/non-keyed/react-hookstate/index.html b/frameworks/non-keyed/react-hookstate/index.html new file mode 100644 index 000000000..d200761ca --- /dev/null +++ b/frameworks/non-keyed/react-hookstate/index.html @@ -0,0 +1,12 @@ + + + + + React Hookstate + + + +
+ + + diff --git a/frameworks/non-keyed/react-hookstate/package.json b/frameworks/non-keyed/react-hookstate/package.json new file mode 100644 index 000000000..93b4ccacd --- /dev/null +++ b/frameworks/non-keyed/react-hookstate/package.json @@ -0,0 +1,39 @@ +{ + "name": "js-framework-benchmark-react-hooks", + "version": "1.1.1", + "description": "React Hooks demo", + "main": "index.js", + "js-framework-benchmark": { + "frameworkVersionFromPackage": "react:@hookstate/core" + }, + "scripts": { + "build-dev": "webpack --watch", + "build-prod": "webpack" + }, + "keywords": [ + "react", + "webpack" + ], + "author": "Stefan Krause", + "license": "Apache-2.0", + "homepage": "https://github.com/krausest/js-framework-benchmark", + "repository": { + "type": "git", + "url": "https://github.com/krausest/js-framework-benchmark.git" + }, + "devDependencies": { + "@babel/core": "7.4.5", + "@babel/preset-env": "7.4.5", + "@babel/preset-react": "7.0.0", + "@babel/plugin-proposal-class-properties": "7.4.4", + "babel-loader": "8.0.6", + "terser-webpack-plugin": "1.3.0", + "webpack": "4.34.0", + "webpack-cli": "3.3.4" + }, + "dependencies": { + "@hookstate/core": "^1.5.2", + "react": "16.8.6", + "react-dom": "16.8.6" + } +} diff --git a/frameworks/non-keyed/react-hookstate/src/main.jsx b/frameworks/non-keyed/react-hookstate/src/main.jsx new file mode 100644 index 000000000..ab53731f7 --- /dev/null +++ b/frameworks/non-keyed/react-hookstate/src/main.jsx @@ -0,0 +1,129 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { useStateLink, createStateLink, None, Downgraded } from '@hookstate/core'; + +function random(max) { return Math.round(Math.random() * 1000) % max; } + +const A = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean", + "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", + "cheap", "expensive", "fancy"]; +const C = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"]; +const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", + "keyboard"]; + +let nextId = 1; + +function buildData(count) { + const data = {}; + for (let i = 0; i < count; i++) { + data[nextId] = { + id: nextId, + label: `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`, + }; + nextId += 1; + } + return data; +} + +const globalState = createStateLink({}); +let selectedState = undefined; + +const GlyphIcon = ; + +const Row = ({ itemState }) => { + const state = useStateLink(itemState).with(Downgraded); + const item = state.value; + const select = () => { + if (selectedState && selectedState.nested) { + selectedState.nested.selected.set(false) + } + selectedState = state + selectedState.nested.selected.set(true) + }; + const remove = () => state.set(None); + + return ( + {item.id} + {item.label} + {GlyphIcon} + + ); +} + +const Button = ({ id, cb, title }) => ( +
+ +
+); + +const Jumbotron = () => { + const dataState = globalState + + return (
+
+
+

React Hookstate keyed

+
+
+
+
+
+
+
) +} + +const Rows = () => { + const dataState = useStateLink(globalState); + + return ( + {dataState.keys.map(itemKey => + + )} +
) +} + +const Main = () => { + return (
+ + + +
); +} + +ReactDOM.render(
, document.getElementById('main')); diff --git a/frameworks/non-keyed/react-hookstate/webpack.config.js b/frameworks/non-keyed/react-hookstate/webpack.config.js new file mode 100644 index 000000000..f6607fcc0 --- /dev/null +++ b/frameworks/non-keyed/react-hookstate/webpack.config.js @@ -0,0 +1,78 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); + +module.exports = { + mode: 'production', + // mode: 'development', + entry: { + main: path.join(__dirname, 'src', 'main.jsx'), + }, + output: { + path: path.join(__dirname, 'dist'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.jsx'] + }, + module: { + rules: [{ + test: /\.jsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env', '@babel/preset-react'], + plugins: ['@babel/plugin-proposal-class-properties'], + } + } + ] + }] + }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + parse: { + // we want terser to parse ecma 8 code. However, we don't want it + // to apply any minfication steps that turns valid ecma 5 code + // into invalid ecma 5 code. This is why the 'compress' and 'output' + // sections only apply transformations that are ecma 5 safe + // https://github.com/facebook/create-react-app/pull/4234 + ecma: 8, + }, + compress: { + ecma: 5, + warnings: false, + // Disabled because of an issue with Uglify breaking seemingly valid code: + // https://github.com/facebook/create-react-app/issues/2376 + // Pending further investigation: + // https://github.com/mishoo/UglifyJS2/issues/2011 + comparisons: false, + }, + mangle: { + safari10: true, + }, + output: { + ecma: 5, + comments: false, + // Turned on because emoji and regex is not minified properly using default + // https://github.com/facebook/create-react-app/issues/2488 + ascii_only: true, + }, + }, + // Use multi-process parallel running to improve the build speed + // Default number of concurrent runs: os.cpus().length - 1 + parallel: true, + // Enable file caching + cache: true, + }), + ] + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { NODE_ENV: JSON.stringify('production') } + }), + ], +};