Skip to content

feat: add effector-react #725

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions frameworks/keyed/effector-react/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React</title>
<link href="/css/currentStyle.css" rel="stylesheet"/>
</head>
<body>
<div id='main'></div>
<script src='dist/main.js'></script>
</body>
</html>
40 changes: 40 additions & 0 deletions frameworks/keyed/effector-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "js-framework-benchmark-effector-react",
"version": "0.1.0",
"description": "Effector-React demo",
"main": "index.js",
"js-framework-benchmark": {
"frameworkVersionFromPackage": "effector-react"
},
"scripts": {
"build-dev": "webpack --watch",
"build-prod": "webpack"
},
"keywords": [
"react",
"webpack"
],
"author": "Sergey Sova",
"license": "MIT",
"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": {
"effector": "^20.14.0",
"effector-react": "^20.7.1",
"react": "16.8.6",
"react-dom": "16.8.6"
}
}
244 changes: 244 additions & 0 deletions frameworks/keyed/effector-react/src/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { createStore, createEvent, sample } from "effector";
import { useList, useStoreMap } from "effector-react";

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 = new Array(count);
for (let i = 0; i < count; i++) {
data[i] = {
id: nextId++,
label: `${A[random(A.length)]} ${C[random(C.length)]} ${
N[random(N.length)]
}`,
};
}
return data;
}

const GlyphIcon = (
<span className="glyphicon glyphicon-remove" aria-hidden="true"></span>
);

class Row extends React.Component {
onSelect = () => {
this.props.select(this.props.item);
};

onRemove = () => {
this.props.remove(this.props.item);
};

shouldComponentUpdate(nextProps) {
return (
nextProps.item !== this.props.item ||
nextProps.selected !== this.props.selected
);
}

render() {
let { selected, item } = this.props;
return (
<tr className={selected ? "danger" : ""}>
<td className="col-md-1">{item.id}</td>
<td className="col-md-4">
<a onClick={this.onSelect}>{item.label}</a>
</td>
<td className="col-md-1">
<a onClick={this.onRemove}>{GlyphIcon}</a>
</td>
<td className="col-md-6"></td>
</tr>
);
}
}

function Button({ id, cb, title }) {
return (
<div className="col-sm-6 smallpad">
<button
type="button"
className="btn btn-primary btn-block"
id={id}
onClick={cb}
>
{title}
</button>
</div>
);
}

class Jumbotron extends React.Component {
shouldComponentUpdate() {
return false;
}

render() {
const { run, runLots, add, update, clear, swapRows } = this.props;
return (
<div className="jumbotron">
<div className="row">
<div className="col-md-6">
<h1>React keyed</h1>
</div>
<div className="col-md-6">
<div className="row">
<Button id="run" title="Create 1,000 rows" cb={run} />
<Button id="runlots" title="Create 10,000 rows" cb={runLots} />
<Button id="add" title="Append 1,000 rows" cb={add} />
<Button id="update" title="Update every 10th row" cb={update} />
<Button id="clear" title="Clear" cb={clear} />
<Button id="swaprows" title="Swap Rows" cb={swapRows} />
</div>
</div>
</div>
</div>
);
}
}

const $data = createStore([]);
const $selected = createStore(0);

const run = createEvent();
const runLots = createEvent();
const add = createEvent();
const update = createEvent();
const select = createEvent(); // item
const remove = createEvent(); // item
const clear = createEvent();
const swapRows = createEvent();

$data
.on(run, () => buildData(1000))
.on(runLots, () => buildData(10000))
.on(add, (list) => list.concat(buildData(1000)))
.on(update, (list) => {
const data = list.concat([]); // to change ref to arrays
for (let i = 0; i < data.length; i += 10) {
const item = data[i];
data[i] = { id: item.id, label: item.label + " !!!" };
}
return data;
})
.on(remove, (list, item) => {
const data = list.concat([]); // to change ref
data.splice(data.indexOf(item), 1);
return data;
})
.on(clear, () => [])
.on(swapRows, (list) => {
const data = list.concat([]); // to change ref
if (data.length > 998) {
let temp = data[1];
data[1] = data[998];
data[998] = temp;
}
return data;
});

// @ts-ignore
$selected.on(select, (_, item) => item.id).on(clear, () => 0);

function Main() {
return (
<div className="container">
<Jumbotron
run={run}
runLots={runLots}
add={add}
update={update}
clear={clear}
swapRows={swapRows}
/>
<table className="table table-hover table-striped test-data">
<tbody>
{useList($data, (item) => {
const isSelected = useStoreMap({
store: $selected,
keys: [item.id],
fn: (selected, [id]) => selected === id,
});
return (
<Row
key={item.id}
item={item}
selected={isSelected}
select={select}
remove={remove}
></Row>
);
})}
</tbody>
</table>
<span
className="preloadicon glyphicon glyphicon-remove"
aria-hidden="true"
></span>
</div>
);
}

ReactDOM.render(<Main />, document.getElementById("main"));
83 changes: 83 additions & 0 deletions frameworks/keyed/effector-react/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
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",
"effector/babel-plugin",
],
},
},
],
},
],
},
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") },
}),
],
};