Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
philipp-spiess committed Jun 2, 2018
1 parent d3eed7a commit 2fdaaf1
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 222 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["env"]
"presets": ["env", "react"]
}
35 changes: 35 additions & 0 deletions __tests__/__snapshots__/index-test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ReComponent with Immutable.JS records increases the counter when clicked 1`] = `
<button>
You've clicked this
1
times(s)
</button>
`;

exports[`ReComponent with Immutable.JS records renders the initial state 1`] = `
<button>
You've clicked this
0
times(s)
</button>
`;

exports[`ReComponent with mocked console errors when no \`reducer\` method is defined 1`] = `"Example(...): No \`reducer\` method found on the returned component instance: did you define a reducer?"`;

exports[`ReComponent with plain JS objects increases the counter when clicked 1`] = `
<button>
You've clicked this
1
times(s)
</button>
`;

exports[`ReComponent with plain JS objects renders the initial state 1`] = `
<button>
You've clicked this
0
times(s)
</button>
`;
187 changes: 104 additions & 83 deletions __tests__/index-test.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,117 @@
import { PID, spawn, isAlive, self, receive, send } from "../";
import React from "react";
import ReactDOM from "react-dom";
import { Record } from "immutable";

describe("process", () => {
describe("spawn", () => {
it("runs the process", done => {
spawn(function* () { done() });
import { ReComponent } from "../";

global.__DEV__ = true;

function click(element) {
element.dispatchEvent(new Event("click", { bubbles: true }));
}

describe("ReComponent", () => {
let container;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});

describe("with plain JS objects", () => {
class Example extends ReComponent {
constructor() {
super();
this.handleClick = this.createDispatcher("CLICK");
}

initialState(props) {
return {
count: 0
};
}

reducer(action, state) {
switch (action.type) {
case "CLICK":
return { count: state.count + 1 };
}
}

render() {
return (
<button onClick={this.handleClick}>
You've clicked this {this.state.count} times(s)
</button>
);
}
}

it("renders the initial state", () => {
const instance = ReactDOM.render(<Example />, container);
expect(container.firstChild).toMatchSnapshot();
});

it("returns a pid", () => {
const pid = spawn(function* () {});
expect(pid).toBeInstanceOf(PID);
it("increases the counter when clicked", () => {
const instance = ReactDOM.render(<Example />, container);
click(container.firstChild);
expect(container.firstChild).toMatchSnapshot();
});
});

describe("isAlive", () => {
it("returns true for another process", () => {
const pid = spawn(function* () { return });
expect(isAlive(pid)).toBe(true);
describe("with Immutable.JS records", () => {
const State = Record({ count: 0 });
class Example extends ReComponent {
constructor() {
super();
this.handleClick = this.createDispatcher("CLICK");
}

initialState(props) {
return State();
}

reducer(action, state) {
switch (action.type) {
case "CLICK":
return state.update("count", count => count + 1);
}
}

render() {
return (
<button onClick={this.handleClick}>
You've clicked this {this.immutableState.count} times(s)
</button>
);
}
}

it("renders the initial state", () => {
const instance = ReactDOM.render(<Example />, container);
expect(container.firstChild).toMatchSnapshot();
});
});

describe("send and receive", () => {
it("can receive a messages on the main process that was already sent", async () => {
send(self(), {
type: "test",
payload: { key: "value" }
});

const message = await receive("test");
expect(message).toEqual({
type: "test",
payload: { key: "value" }
});
it("increases the counter when clicked", () => {
const instance = ReactDOM.render(<Example />, container);
click(container.firstChild);
expect(container.firstChild).toMatchSnapshot();
});
});

it("can receive a messages on the main process that will be sent", async () => {
setTimeout(() => {
send(self(), {
type: "test",
payload: { key: "value" }
});
}, 0);

const message = await receive("test");
expect(message).toEqual({
type: "test",
payload: { key: "value" }
});
describe("with mocked console", () => {
const originalError = console.error;
beforeEach(() => (console.error = jest.fn()));
afterEach(() => (console.error = originalError));
it("errors when no `reducer` method is defined", () => {
class Example extends ReComponent {
render() {
return <div />;
}
}

expect(() => {
ReactDOM.render(<Example />, container);
}).toThrowErrorMatchingSnapshot();
});
});
});

// class CountServer extends GenServer {
// getInitialState(initialProps) {
// this.state = { counter: initialProps.counter || 0 }
// }

// // async
// handleCast(action) {
// switch (action.type) {
// case "INCREMENT":
// this.setState(({ count }) => {
// count: count + 1;
// });
// break;
// case "DECREMENT":
// this.setState(({ count }) => {
// count: count - 1;
// });
// break;
// }
// }

// // sync
// handleCall(action) {
// switch (action.type) {
// case "INCREMENT":
// this.setState(({ count }) => {
// count: count + 1;
// });
// break;
// case "DECREMENT":
// this.setState(({ count }) => {
// count: count - 1;
// });
// break;
// }
// }
// }
//
// describe("react-genserver", () => {
// it("works", () => {
// const pid = start(CountServer)
// console.log(pid);
// });
// });
15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
{
"name": "react-genserver",
"version": "1.0.0",
"name": "react-recomponent",
"version": "0.0.1",
"main": "src/index.js",
"license": "MIT",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-jest": "^23.0.1",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"jest": "^23.1.0",
"prettier": "^1.13.4"
"prettier": "^1.13.4",
"react": "^16.4.0",
"react-dom": "^16.4.0",
"react-test-renderer": "^16.4.0"
},
"dependencies": {
"erlang-processes": "^2.0.0",
"erlang-types": "^1.0.1"
"peerDependencies": {
"react": "^16.4.0"
}
}
67 changes: 49 additions & 18 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,55 @@
// export { self } from './pid'
export { start, self, spawn, send, receive, isAlive } from "./process";
import React from "react";

export {PID } from "erlang-types"
// export class GenServer {
// constructor(initialProps) {
// this.getInitialState && this.setState(this.getInitialState(initialProps));
// }
import { isImmutable } from "./isImmutable";

// setState(updater) {
// let nextState = updater;
// if (typeof updater == "function") {
// nextState = updater(this.state);
// }
export class ReComponent extends React.Component {
constructor(props) {
super(props);

// this.state = Object.assign({}, this.state, nextState);
// }
if (__DEV__) {
if(typeof this.reducer !== 'function') {
const name = this.constructor.name || this.displayName
throw new Error(name + '(...): No `reducer` method found on the returned component ' +
'instance: did you define a reducer?')
}
}

// cast() {}
let stateIsImmutable = false;
if (this.initialState) {
let initialState = this.initialState(props);

// call() {}
// }
if (isImmutable(initialState)) {
stateIsImmutable = true;
initialState = { immutableState: initialState };

// export { start, spawn, send, receive };
// Define Immutable.js helpers
this.setImmutableState = updater => {
this.setState({ immutableState: updater(this.immutableState) });
};
Object.defineProperty(this, "immutableState", {
get: () => this.state.immutableState
});
}

this.state = initialState;
}

this.send = action => {
const reduce = state => this.reducer(action, state)

if (stateIsImmutable) {
this.setImmutableState(reduce);
} else {
this.setState(reduce);
}
};

this.createDispatcher = type => {
return payload =>
this.send({
type,
payload
});
};
}
}
5 changes: 0 additions & 5 deletions src/invariant.js

This file was deleted.

23 changes: 23 additions & 0 deletions src/isImmutable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @see https://github.com/facebook/immutable-js/blob/v4.0.0-rc.9/src/Predicates.js
*/

export function isImmutable(maybeImmutable) {
return isCollection(maybeImmutable) || isRecord(maybeImmutable);
}

function isCollection(maybeCollection) {
return !!(maybeCollection && maybeCollection[IS_ITERABLE_SENTINEL]);
}

function isRecord(maybeRecord) {
return !!(maybeRecord && maybeRecord[IS_RECORD_SENTINEL]);
}

export const IS_ITERABLE_SENTINEL = "@@__IMMUTABLE_ITERABLE__@@";
export const IS_RECORD_SENTINEL = "@@__IMMUTABLE_RECORD__@@";
Loading

0 comments on commit 2fdaaf1

Please sign in to comment.