This repository has been archived by the owner on Sep 21, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0e173b7
Showing
13 changed files
with
408 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Oops, something went wrong.