Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed May 6, 2014
0 parents commit 0e173b7
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 0 deletions.
52 changes: 52 additions & 0 deletions 01 - Core/01 - Classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Instead of using @jsx directive we import the HTML export from react-dom
// and bind it to the JSX identifier. This models the DOM dependency explicitly.
// The JSX transpiler uses this identifier to resolve HTML tags. E.g. JSX.div();
import { HTML: JSX } from "react-dom";

// The only reason we need a React depencency here is because the base class
// provides the this.setState method.
import { Component } from "react";

// We can inline a named export call.
export class Button extends Component {

// Prop types are defined using built-in language support using a TypeScript
// compatible syntax. Notice the subtle syntax difference between the colon
// and the equal sign.
props : {
width: number
}

// Default properties can be defined as a static property initializer.
// The value of each property is shallow copied onto the final props object.
static defaultProps = {
width: 100
}

// Initial state is defined using a property initializer. In this simple
// form it behaves identical to TypeScript. You may refer to this.props
// within this initializer to make intial state a function of props.
state = {
counter: Math.round(this.props.width / 10)
}

// Instead of relying on auto-binding magic inside React, we use a property
// initializer with an arrow function. This effectively creates a single
// bound method per instance - the same as auto-binding.
handleClick = (event) => {
event.preventDefault();
this.setState({ counter: this.state.counter + 1 });
}

// Props and state are passed into render as a convenience to avoid the need
// for aliasing or referring to `this`.
render(props, state) {
return (
<div>
This button has been clicked: {state.counter} times
<button onClick={this.handleClick} style={{ width: props.width }} />
</div>
);
}

}
70 changes: 70 additions & 0 deletions 01 - Core/02 - Mixins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Component } from "react";
import { HTML: JSX } from "react-dom";
import { mixin } from "react-utils";

// Chainable mixins

const A = {

componentDidMount() {
super(); // This will end up calling an empty function, placed by mixin()
console.log('A');
}

};

class B {

static getQueries() {
super(); // This will end up calling an empty function, placed by mixin()
console.log('B')
}

componentDidMount() {
console.log('B');
super(); // This will end up calling A.componentDidMount
}

}

class C extends mixin(A, B) {

static getQueries() {
super(); // This calls B.getQueries
console.log('C');
}

componentDidMount() {
super(); // This calls B.prototype.componentDidMount
console.log('C');
}

}

C.getQueries(); // B, C
new C().componentDidMount(); // B, A, C

// A component that mixes in all of C's functionality

class Component extends mixin(React.Component, C) {
render() {
return <div />;
}
}

// Solvable but confusing/complex issues:

export class C extends mixin(A, B) {

// This state intializer overrides the state initializer in the base class.
// The current React class system merges the two.
state = {
b: true
}

componentDidMount() {
// You forgot to put a super call here but there's no runtime warning since
// the mixin logic happens before this class is created.
}

}
29 changes: 29 additions & 0 deletions 01 - Core/03 - Stateless Functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Instead of using @jsx directive we import the HTML export from react-dom
// and bind it to the JSX identifier.
import { HTML: JSX } from "react-dom";

// A simple component, that isn't stateful, can be provided as a single
// function that accepts props. This provides React with a hint that this
// component can be collapsed and that it's state doesn't need to be preserved.
// It also encourages micro-componentization instead of custom helper functions
// outside the system.
export function Button(props : { width: number, onClick: function }) {
return (
<div>
Fancy button
<button onClick={props.onClick} style={{ width: props.width }} />
</div>
);
}

// When named exports are used, it may be valid to have multiple components
// in the same file. Destructuring can be used to provide convenience aliasing
// and defaults to props.
export function Checkbox({ checked = true, width }) {
return (
<div>
Fancy checkbox
<input type="checkbox" checked={checked} style={{ width }} />
</div>
);
}
24 changes: 24 additions & 0 deletions 01 - Core/04 - Modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Through out the other examples named exports are used.

export class Button extends Component {

}
// This means that components are imported using the following syntax:

import { Button } from "Button";

// and can be aliases like this:

import { Button: MySpecialButton } from "Button";

// Another alternative is to export defaults. It's unclear which of these two
// paradigms will win for Component modules. If default exports win popular
// mindshare, we might encourage a different syntax for exports:

export default class Button extends Component {
// ...
}

// To import a default export you would simply leave out the curly braces.

import MySpecialButton from "Button";
62 changes: 62 additions & 0 deletions 01 - Core/05 - Descriptor Factories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// The remaining files in this repo all assume a slight change to the semantics
// of JSX. The runtime semantics of the following JSX expression:

<Button bar="baz" />

// Would turn into an object initializer of a descriptor:

{
type: Button,
props: { bar: "baz", anotherProp: "it's default value" },
scope: this,
// It could potentially be tagged with a shared prototype.
__proto__: JSX.Descriptor
}

// We still have an opportunity to add static analysis, validation and
// optimizations in the transform but semantically it means the same.

// This means that there is no need for the class Button to export any helper
// function to create these descriptors. A simple React class would look
// something like this:

import InnerButton from "inner-button";

export class Button {
render() {
return <InnerButton />;
}
};

// Note that there is no dependency on React's runtime nor the concept of
// descriptors in this scenario.

// Strings can be used for registered WebComponents and HTML/SVG DOM nodes
// in a JSON compatible structure to describe a tree.

{ type: 'div', props: { className: 'foo', children: [
{ type: 'span', props: { children:
'text content'
} }
] } }

// That allows the user to code arbitrary DOM centric components without any
// dependency on the React runtime itself. Avoiding the need for mocking or
// stubbing.

// The JSON syntax can be fairly inconvenient without JSX though. In this
// scenario we would recommend that you explicitly export a descriptor factory
// from all your components.

import React from "react";

class Button {

}

export default React.createComponent(Button);

// The major downside of this proposal is that it potentially leads to
// fragmentation in the community where certain components are exported with
// descriptor factories and not. If you don't use JSX, you may have to create
// your own wrapper factories around third party components.
83 changes: 83 additions & 0 deletions 01 - Core/06 - Refs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@

class Foo {

// A ref is declared
myDivRef = React.createRef();

// Promise API for refs
handleTick() {
this.myDivRef.then(myDivNode => {
this.setState({ width: myDivNode.offsetWidth });
});
}

// Alternative syntax using future ES7 async/await syntax
async handleTick() {
this.setState({ width: (await this.myDivRef).offsetWidth });
}

render() {
return (
<C tick={this.handleTick}>
<div ref={this.myDivRef} />
<CustomComponent context={this.myDivRef} />
</C>
);
}

}

class Foo {
refs = new React.RefMap();

handleTick() {
this.refs.get('myDiv').then(myDivNode => {
this.setState({ width: myDivNode.offsetWidth });
});
}

render() {
return (
<C tick={this.handleTick}>
<div ref={this.refs.get('myDiv')} />
<CustomComponent context={this.refs.get('myDiv')} />
</C>
);
}
}

// We also probably need to provide a synchronous API as an upgrade path:

handleTick() {
var myDivNode = this.myDivRef.doExpensiveWorkRightNowAndLetMeHaveMyNodeNow();
this.setState({ width: myDivNode.offsetWidth });
}

handleTick() {
this.refs.get('myDiv').then(myDivNode => {
this.setState({ width: myDivNode.offsetWidth });
}, error => {
this.setState({ width: 0 });
});
}


// An alternative idea... Make descriptors into ref-promises.

render() {
var foo = <Foo />;
return <div onClick={() => foo.then(inst => inst.doX())}>{foo}</div>;
}

// This also provides a nice reset functionality if the ref is ever swapped out.

handleClick() {
this.foo.then(inst => inst.doX());
}

render() {
this.foo = <Foo />; // ugh side-effect in render (puke)
return <div onClick={this.handleClick}>{this.foo}</div>;
}

// This also works nice with multiple owners.
20 changes: 20 additions & 0 deletions 01 - Core/07 - Imperative Bridge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Button } from "button";
import { Renderer } from "react-dom";

// Currently setProps() provides a convenient hook on rendered components
// that are top level. This magically turns props into state instead of being
// modeled outside the component. This undermines the integrity of props
// in a component tree. Instead, we want to add a wrapper that saves these
// values as it's own internal state. You can imperatively update the
// props using setters on the renderer instances. These gets flushed down to
// the underlying component class.

var button = new Renderer(<Button foo="bar" />);

// Insertion is done by exposing a rendered top-level element which can be
// inserted anywhere in the DOM.

document.body.appendChild(button.toElement());

// A setter is used to update the component
button.foo = "baz";
3 changes: 3 additions & 0 deletions 02 - Web Components/TBD
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This section is not yet complete.

Please come back later or send a pull request with your own ideas.
3 changes: 3 additions & 0 deletions 03 - Animations/TBD
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This section is not yet complete.

Please come back later or send a pull request with your own ideas.
3 changes: 3 additions & 0 deletions 04 - Layout/TBD
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This section is not yet complete.

Please come back later or send a pull request with your own ideas.
3 changes: 3 additions & 0 deletions 05 - Embedded Queries/TBD
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This section is not yet complete.

Please come back later or send a pull request with your own ideas.
3 changes: 3 additions & 0 deletions 06 - Far Future/TBD
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This section is not yet complete.

Please come back later or send a pull request with your own ideas.
Loading

0 comments on commit 0e173b7

Please sign in to comment.