├── .gitignore
├── README.md
├── appendix
├── 0-Terminology.md
├── 1-Rendering-A-Top-Level-Element.md
├── 2-Reconcile-Transaction.md
└── 3-Host-Environment-Examples.md
├── example.js
├── package.json
├── src
├── fiber-types
│ ├── .flowconfig
│ ├── React.js.flow
│ ├── ReactCompositeComponentTypes.js.flow
│ ├── ReactCoroutine.js.flow
│ ├── ReactElementType.js.flow
│ ├── ReactFiber.js.flow
│ ├── ReactFiberReconciler.js.flow
│ ├── ReactFiberRoot.js.flow
│ ├── ReactFiberUpdateQueue.js.flow
│ ├── ReactInstanceType.js.flow
│ ├── ReactPortal.js.flow
│ ├── ReactPriorityLevel.js.flow
│ ├── ReactTypeOfSideEffect.js.flow
│ ├── ReactTypeOfWork.js.flow
│ ├── ReactTypes.js.flow
│ └── package.json
├── fiber
│ ├── .flowconfig
│ ├── HowDoesFiberWork.md
│ ├── README.md
│ ├── ReactTinyFiber.js
│ ├── ReactTinyTypes.js
│ ├── index.js
│ ├── package.json
│ └── yarn.lock
└── stack
│ ├── README.md
│ ├── component.js
│ ├── index.js
│ ├── injection.js
│ ├── mount.js
│ ├── package.js
│ ├── reconcileTransaction.js
│ ├── utilities
│ ├── invariants.js
│ └── serialize.js
│ └── yarn.lock
├── test.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tiny React Renderer
2 |
3 | Creating a React Renderer will give you the opportunity to apply the same React
4 | knowledge that you and your team already know and enjoy from the web and native
5 | to whatever host environment you need.
6 |
7 | Creating a renderer is a fairly straight-forward affair once you know what
8 | you’re looking for.
9 |
10 | Many languages have this concept of a `main`—the entry point to your
11 | application. If you look at any React application code you’ve written you’ll see
12 | that you “start” your app with a call like the following:
13 |
14 | ```jsx
15 | // web
16 | ReactDOM.render(React.createElement(MyApp), document.getElementById('app'));
17 |
18 | // native
19 | AppRegistry.registerComponent('MyApp', () => MyApp);
20 | ```
21 |
22 | This is where your application enters into the React domain and comes alive. Your
23 | root React element is instantiated and attached to the host environment.
24 |
25 | If you follow either the ReactDOM or React Native codebases from where these
26 | methods are defined you will quickly find yourself at the `React{Host}Mount.js`
27 | file. Our renderer also begins there.
28 |
29 | With that let’s get started! Our tour continues in [./src/stack/mount.js](./src/stack/mount.js).
30 |
31 | ## Work in Progress
32 |
33 | Please note this guide is a work in progress. Much of this knowledge is derived
34 | from my experience in creating [React Hardware](https://github.com/iamdustan/react-hardware).
35 |
36 | ## Tests
37 |
38 | * `npm test` will run the tests with the stack-based renderer
39 | * `npm test -- --fiber` will run the tests with the upcoming fiber implementation
40 |
41 | ## Renderer Implementations
42 |
43 | * Stack
44 | * Fiber
45 |
46 | The React that we have worked with to date has been using what is called the
47 | stack renderer. This is because it is implemented on the traditional JavaScript
48 | stack and operates within those confines.
49 |
50 | Fiber, on the other-hand, can be thought of like a React VM. It is influenced by
51 | OCaml, algebraic effects, and other functional ideas. From an implementation
52 | level, Fiber has a first-class renderer API allowing creating custom renderers a
53 | much nicer experience.
54 |
55 | The documentation for stack is no in maintenance mode and will not be very
56 | accurate as time progresses. This can be found in `./src/stack`. The Fiber API
57 | changes will be followed throughout its development to a stable release in
58 | `./src/fiber`
59 |
60 | ## Thanks
61 |
62 | * [@thejameskyle](https://github.com/thejameskyle): for the inspiration of repo style
63 | * [@ryanflorence](https://github.com/ryanflorence) and [@mjackson](https://github.com/mjackson) for React Router and the problem that inspired this
64 | * [@gaearon](https://github.com/gaearon), [@matthewwithanm](https://github.com/matthewwithanm),
65 | [@vjeux](https://github.com/vjeux), [@zpao](https://github.com/zpao),
66 | [@Yomguithereal](https://github.com/Yomguithereal), [@axemclion](https://github.com/axemclion),
67 | and everyone else who has helped me poke around the React codebase.
68 |
--------------------------------------------------------------------------------
/appendix/0-Terminology.md:
--------------------------------------------------------------------------------
1 | # Terminology
2 |
3 | Required reading:
4 | * [React Components, Elements, and Instances](https://facebook.github.io/react/blog/2015/12/18/react-components-elements-and-instances.html)
5 | * [React – Basic Theoretical Concepts](https://github.com/reactjs/react-basic)
6 |
7 | Coming soon. PRs also accepted :smile:
8 |
9 | tldr;
10 |
11 | * ReactElement: The return value of `` /
12 | `React.createElement('jsx', props)` calls
13 | * Component: There is the public React.Component interface that users interact
14 | with. There is a mirror internal component class which is responsible for the
15 | managing the internal state, lifecycle, warnings, invariants, props, et cetera.
16 | * Instance: ...
17 |
18 |
--------------------------------------------------------------------------------
/appendix/1-Rendering-A-Top-Level-Element.md:
--------------------------------------------------------------------------------
1 | # Rendering a Top Level Element
2 |
3 | Coming soon. PRs also accepted :smile:
4 |
5 |
--------------------------------------------------------------------------------
/appendix/2-Reconcile-Transaction.md:
--------------------------------------------------------------------------------
1 | # Reconcile Transaction
2 |
3 | Coming soon. PRs also accepted :smile:
4 |
5 |
--------------------------------------------------------------------------------
/appendix/3-Host-Environment-Examples.md:
--------------------------------------------------------------------------------
1 | # Host Environment Example
2 |
3 | Coming soon. PRs also accepted :smile:
4 |
5 |
--------------------------------------------------------------------------------
/example.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const {
3 | Router,
4 | Route,
5 | Link,
6 | browserHistory,
7 | renderToJSON
8 | } = require('react-router');
9 |
10 | console.log(
11 | ReactDOM.render(
12 | renderToJSON(
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | )
21 | )
22 | );
23 |
24 | /**
25 | * { path: '/',
26 | * component: App,
27 | * childRoutes: [
28 | * ]
29 | * ...
30 | * }
31 | */
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tiny-react-renderer",
3 | "version": "0.2.0",
4 | "description": "A tiny React renderer to demonstrate how to write a renderer.",
5 | "main": "./index.js",
6 | "scripts": {
7 | "flow": "pushd src/fiber; flow; popd;",
8 | "test-stack": "node ./test --stack",
9 | "test-fiber": "node ./test --fiber",
10 | "test": "node ./test && node ./test --fiber && npm run flow"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/iamdustan/tiny-react-renderer"
15 | },
16 | "files": [
17 | "src",
18 | "package.json",
19 | "README.md"
20 | ],
21 | "keywords": [
22 | "react",
23 | "reactjs",
24 | "renderer"
25 | ],
26 | "author": "Dustan Kasten ",
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/iamdustan/tiny-render-renderer/issues"
30 | },
31 | "homepage": "https://github.com/iamdustan/tiny-render-renderer",
32 | "babel": {
33 | "plugins": [
34 | "transform-flow-strip-types"
35 | ]
36 | },
37 | "dependencies": {
38 | "babel-register": "^6.23.0",
39 | "fbjs": "^0.8.4",
40 | "react": "15.3.x"
41 | },
42 | "devDependencies": {
43 | "babel-plugin-transform-flow-strip-types": "^6.22.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/fiber-types/.flowconfig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamdustan/tiny-react-renderer/2337d6558f0206e2b30c38e5df980bb70a046a92/src/fiber-types/.flowconfig
--------------------------------------------------------------------------------
/src/fiber-types/React.js.flow:
--------------------------------------------------------------------------------
1 | /** @flow */
2 |
3 | export type ReactComponent = typeof React$Component;
4 | export type ReactElement = typeof React$Element;
5 |
6 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactCompositeComponentTypes.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactCompositeComponentTypes
10 | * @flow
11 | */
12 |
13 | export type CompositeComponentTypes = 0 | 1 | 2;
14 |
15 | module.exports = {
16 | ImpureClass: 0,
17 | PureClass: 1,
18 | StatelessFunctional: 2,
19 | };
20 |
21 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactCoroutine.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactCoroutine
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | import type { ReactNodeList } from './ReactTypes';
16 |
17 | type ReifiedYield = { continuation: Object, props: Object };
18 | type CoroutineHandler = (props: T, yields: Array) => ReactNodeList;
19 |
20 | export type ReactCoroutine = {
21 | $$typeof: Symbol | number,
22 | key: null | string,
23 | children: any,
24 | // This should be a more specific CoroutineHandler
25 | handler: (props: any, yields: Array) => ReactNodeList,
26 | props: any,
27 | };
28 | export type ReactYield = {
29 | $$typeof: Symbol | number,
30 | key: null | string,
31 | props: Object,
32 | continuation: mixed
33 | };
34 |
35 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactElementType.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @flow
10 | * @providesModule ReactElementType
11 | */
12 |
13 | 'use strict';
14 |
15 | export type Source = {
16 | fileName: string,
17 | lineNumber: number,
18 | };
19 |
20 | export type ReactElement = {
21 | $$typeof: any,
22 | type: any,
23 | key: any,
24 | ref: any,
25 | props: any,
26 | _owner: any, // ReactInstance or ReactFiber
27 |
28 | // __DEV__
29 | _store: {
30 | validated: boolean,
31 | },
32 | _self: ReactElement,
33 | _shadowChildren: any,
34 | _source: Source,
35 | };
36 |
37 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactFiber.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactFiber
10 | * @flow
11 | */
12 | import type { Source } from './ReactElementType';
13 | import type { ReactInstance, DebugID } from './ReactInstanceType';
14 | import type { TypeOfWork } from './ReactTypeOfWork';
15 | import type { TypeOfSideEffect } from './ReactTypeOfSideEffect';
16 | import type { PriorityLevel } from './ReactPriorityLevel';
17 | import type { UpdateQueue } from './ReactFiberUpdateQueue';
18 |
19 |
20 | // A Fiber is work on a Component that needs to be done or was done. There can
21 | // be more than one per component.
22 | export type Fiber = {
23 | // __DEV__ only
24 | _debugID ?: DebugID,
25 | _debugSource ?: Source | null,
26 | _debugOwner ?: Fiber | ReactInstance | null, // Stack compatible
27 |
28 | // These first fields are conceptually members of an Instance. This used to
29 | // be split into a separate type and intersected with the other Fiber fields,
30 | // but until Flow fixes its intersection bugs, we've merged them into a
31 | // single type.
32 |
33 | // An Instance is shared between all versions of a component. We can easily
34 | // break this out into a separate object to avoid copying so much to the
35 | // alternate versions of the tree. We put this on a single object for now to
36 | // minimize the number of objects created during the initial render.
37 |
38 | // Tag identifying the type of fiber.
39 | tag: TypeOfWork,
40 |
41 | // Unique identifier of this child.
42 | key: null | string,
43 |
44 | // The function/class/module associated with this fiber.
45 | type: any,
46 |
47 | // The local state associated with this fiber.
48 | stateNode: any,
49 |
50 | // Conceptual aliases
51 | // parent : Instance -> return The parent happens to be the same as the
52 | // return fiber since we've merged the fiber and instance.
53 |
54 | // Remaining fields belong to Fiber
55 |
56 | // The Fiber to return to after finishing processing this one.
57 | // This is effectively the parent, but there can be multiple parents (two)
58 | // so this is only the parent of the thing we're currently processing.
59 | // It is conceptually the same as the return address of a stack frame.
60 | return: Fiber | null,
61 |
62 | // Singly Linked List Tree Structure.
63 | child: Fiber | null,
64 | sibling: Fiber | null,
65 | index: number,
66 |
67 | // The ref last used to attach this node.
68 | // I'll avoid adding an owner field for prod and model that as functions.
69 | ref: null | (((handle : mixed) => void) & { _stringRef: ?string }),
70 |
71 | // Input is the data coming into process this fiber. Arguments. Props.
72 | pendingProps: any, // This type will be more specific once we overload the tag.
73 | // TODO: I think that there is a way to merge pendingProps and memoizedProps.
74 | memoizedProps: any, // The props used to create the output.
75 |
76 | // A queue of state updates and callbacks.
77 | updateQueue: UpdateQueue | null,
78 |
79 | // The state used to create the output
80 | memoizedState: any,
81 |
82 | // Effect
83 | effectTag: TypeOfSideEffect,
84 |
85 | // Singly linked list fast path to the next fiber with side-effects.
86 | nextEffect: Fiber | null,
87 |
88 | // The first and last fiber with side-effect within this subtree. This allows
89 | // us to reuse a slice of the linked list when we reuse the work done within
90 | // this fiber.
91 | firstEffect: Fiber | null,
92 | lastEffect: Fiber | null,
93 |
94 | // This will be used to quickly determine if a subtree has no pending changes.
95 | pendingWorkPriority: PriorityLevel,
96 |
97 | // This value represents the priority level that was last used to process this
98 | // component. This indicates whether it is better to continue from the
99 | // progressed work or if it is better to continue from the current state.
100 | progressedPriority: PriorityLevel,
101 |
102 | // If work bails out on a Fiber that already had some work started at a lower
103 | // priority, then we need to store the progressed work somewhere. This holds
104 | // the started child set until we need to get back to working on it. It may
105 | // or may not be the same as the "current" child.
106 | progressedChild: Fiber | null,
107 |
108 | // When we reconcile children onto progressedChild it is possible that we have
109 | // to delete some child fibers. We need to keep track of this side-effects so
110 | // that if we continue later on, we have to include those effects. Deletions
111 | // are added in the reverse order from sibling pointers.
112 | progressedFirstDeletion: Fiber | null,
113 | progressedLastDeletion: Fiber | null,
114 |
115 | // This is a pooled version of a Fiber. Every fiber that gets updated will
116 | // eventually have a pair. There are cases when we can clean up pairs to save
117 | // memory if we need to.
118 | alternate: Fiber | null,
119 |
120 | // Conceptual aliases
121 | // workInProgress : Fiber -> alternate The alternate used for reuse happens
122 | // to be the same as work in progress.
123 |
124 | };
125 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactFiberReconciler.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactFiberReconciler
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | import type { ReactComponent } from './React';
16 |
17 | import type { Fiber } from './ReactFiber';
18 | import type { FiberRoot } from './ReactFiberRoot';
19 | import type { PriorityLevel } from './ReactPriorityLevel';
20 | import type { ReactNodeList } from './ReactTypes';
21 |
22 | type Deadline = {
23 | timeRemaining : () => number
24 | };
25 |
26 | type OpaqueHandle = Fiber;
27 | type OpaqueRoot = FiberRoot;
28 |
29 | export type HostConfig = {
30 |
31 | getRootHostContext(rootContainerInstance : C) : CX,
32 | getChildHostContext(parentHostContext : CX, type : T) : CX,
33 | getPublicInstance(instance : I | TI) : PI,
34 |
35 | createInstance(
36 | type : T,
37 | props : P,
38 | rootContainerInstance : C,
39 | hostContext : CX,
40 | internalInstanceHandle : OpaqueHandle
41 | ) : I,
42 | appendInitialChild(parentInstance : I, child : I | TI) : void,
43 | finalizeInitialChildren(parentInstance : I, type : T, props : P, rootContainerInstance : C) : boolean,
44 |
45 | prepareUpdate(
46 | instance : I,
47 | type : T,
48 | oldProps : P,
49 | newProps : P,
50 | rootContainerInstance : C,
51 | hostContext : CX
52 | ) : null | PL,
53 | commitUpdate(
54 | instance : I,
55 | updatePayload : PL,
56 | type : T,
57 | oldProps : P,
58 | newProps : P,
59 | internalInstanceHandle : OpaqueHandle
60 | ) : void,
61 | commitMount(instance : I, type : T, newProps : P, internalInstanceHandle : OpaqueHandle) : void,
62 |
63 | shouldSetTextContent(props : P) : boolean,
64 | resetTextContent(instance : I) : void,
65 |
66 | createTextInstance(
67 | text : string,
68 | rootContainerInstance : C,
69 | hostContext : CX,
70 | internalInstanceHandle : OpaqueHandle
71 | ) : TI,
72 | commitTextUpdate(textInstance : TI, oldText : string, newText : string) : void,
73 |
74 | appendChild(parentInstance : I | C, child : I | TI) : void,
75 | insertBefore(parentInstance : I | C, child : I | TI, beforeChild : I | TI) : void,
76 | removeChild(parentInstance : I | C, child : I | TI) : void,
77 |
78 | scheduleAnimationCallback(callback : () => void) : number | void,
79 | scheduleDeferredCallback(callback : (deadline : Deadline) => void) : number | void,
80 |
81 | prepareForCommit() : void,
82 | resetAfterCommit() : void,
83 |
84 | useSyncScheduling ?: boolean,
85 | };
86 |
87 | export type Reconciler = {
88 | createContainer(containerInfo : C) : OpaqueRoot,
89 | updateContainer(
90 | element : ReactNodeList,
91 | container : OpaqueRoot,
92 | parentComponent : ?ReactComponent
93 | ) : void,
94 | performWithPriority(priorityLevel : PriorityLevel, fn : Function) : void,
95 | batchedUpdates(fn : () => A) : A,
96 | unbatchedUpdates(fn : () => A) : A,
97 | syncUpdates(fn : () => A) : A,
98 | deferredUpdates(fn : () => A) : A,
99 |
100 | // Used to extract the return value from the initial render. Legacy API.
101 | getPublicRootInstance(container : OpaqueRoot) : (ReactComponent | TI | I | null),
102 |
103 | // Use for findDOMNode/findHostNode. Legacy API.
104 | findHostInstance(component : Fiber) : I | TI | null,
105 | };
106 |
107 |
108 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactFiberRoot.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactFiberRoot
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | import type { Fiber } from './ReactFiber';
16 | import type { UpdateQueue } from './ReactFiberUpdateQueue';
17 |
18 | export type FiberRoot = {
19 | // Any additional information from the host associated with this root.
20 | containerInfo: any,
21 | // The currently active root fiber. This is the mutable root of the tree.
22 | current: Fiber,
23 | // Determines if this root has already been added to the schedule for work.
24 | isScheduled: boolean,
25 | // The work schedule is a linked list.
26 | nextScheduledRoot: ?FiberRoot,
27 | // Linked list of callbacks to call after updates are committed.
28 | callbackList: ?UpdateQueue,
29 | };
30 |
31 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactFiberUpdateQueue.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactFiberUpdateQueue
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | type UpdateQueueNode = {
16 | partialState: any,
17 | callback: ?Function,
18 | callbackWasCalled: boolean,
19 | isReplace: boolean,
20 | next: ?UpdateQueueNode,
21 | };
22 |
23 | export type UpdateQueue = UpdateQueueNode & {
24 | isForced: boolean,
25 | hasUpdate: boolean,
26 | hasCallback: boolean,
27 | tail: UpdateQueueNode
28 | };
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactInstanceType.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactInstanceType
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | import type {ReactElement} from './ReactElementType';
16 | import type {CompositeComponentTypes} from './ReactCompositeComponentTypes';
17 |
18 | export type DebugID = number;
19 |
20 | export type ReactInstance = {
21 | // Shared
22 | mountComponent: any,
23 | unmountComponent: any,
24 | receiveComponent: any,
25 | getName: () => string,
26 | getPublicInstance: any,
27 | _currentElement: ReactElement,
28 |
29 | // ReactCompositeComponent
30 | performInitialMountWithErrorHandling: any,
31 | performInitialMount: any,
32 | getHostNode: any,
33 | performUpdateIfNecessary: any,
34 | updateComponent: any,
35 | attachRef: (ref: string, component: ReactInstance) => void,
36 | detachRef: (ref: string) => void,
37 | _rootNodeID: number,
38 | _compositeType: CompositeComponentTypes,
39 |
40 | // ReactDOMComponent
41 | _tag: string,
42 |
43 | // instantiateReactComponent
44 | _mountIndex: number,
45 | _mountImage: any,
46 | // __DEV__
47 | _debugID: DebugID,
48 | _warnedAboutRefsInRender: boolean,
49 | };
50 |
51 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactPortal.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactPortal
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | import type { ReactNodeList } from './ReactTypes';
16 |
17 | export type ReactPortal = {
18 | $$typeof: Symbol | number,
19 | key: null | string,
20 | containerInfo: any,
21 | children : ReactNodeList,
22 | // TODO: figure out the API for cross-renderer implementation.
23 | implementation: any,
24 | };
25 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactPriorityLevel.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactPriorityLevel
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
16 |
17 | /*
18 | module.exports = {
19 | NoWork: 0, // No work is pending.
20 | SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects.
21 | TaskPriority: 2, // Completes at the end of the current tick.
22 | AnimationPriority: 3, // Needs to complete before the next frame.
23 | HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.
24 | LowPriority: 5, // Data fetching, or result from updating stores.
25 | OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
26 | };
27 | */
28 |
29 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactTypeOfSideEffect.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactTypeOfSideEffect
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | export type TypeOfSideEffect = 0 | 1 | 2 | 3 | 4 | 8 | 16 | 32 | 64;
16 |
17 | module.exports = {
18 | NoEffect: 0, // 0b0000000
19 | Placement: 1, // 0b0000001
20 | Update: 2, // 0b0000010
21 | PlacementAndUpdate: 3, // 0b0000011
22 | Deletion: 4, // 0b0000100
23 | ContentReset: 8, // 0b0001000
24 | Callback: 16, // 0b0010000
25 | Err: 32, // 0b0100000
26 | Ref: 64, // 0b1000000
27 | };
28 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactTypeOfWork.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactTypeOfWork
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | export type TypeOfWork = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
16 |
17 | module.exports = {
18 | IndeterminateComponent: 0, // Before we know whether it is functional or class
19 | FunctionalComponent: 1,
20 | ClassComponent: 2,
21 | HostRoot: 3, // Root of a host tree. Could be nested inside another node.
22 | HostPortal: 4, // A subtree. Could be an entry point to a different renderer.
23 | HostComponent: 5,
24 | HostText: 6,
25 | CoroutineComponent: 7,
26 | CoroutineHandlerPhase: 8,
27 | YieldComponent: 9,
28 | Fragment: 10,
29 | };
30 |
--------------------------------------------------------------------------------
/src/fiber-types/ReactTypes.js.flow:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactTypes
10 | * @flow
11 | */
12 |
13 | 'use strict';
14 |
15 | import type { ReactElement } from './React';
16 |
17 | import type { ReactCoroutine, ReactYield } from './ReactCoroutine';
18 | import type { ReactPortal } from './ReactPortal';
19 |
20 | export type ReactNode = ReactElement | ReactCoroutine | ReactYield | ReactPortal | ReactText | ReactFragment;
21 |
22 | export type ReactFragment = ReactEmpty | Iterable;
23 |
24 | export type ReactNodeList = ReactEmpty | ReactNode;
25 |
26 | export type ReactText = string | number;
27 |
28 | export type ReactEmpty = null | void | boolean;
29 |
30 |
--------------------------------------------------------------------------------
/src/fiber-types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-fiber-types",
3 | "version": "16.0.0-alpha.3",
4 | "description": "eact is a JavaScript library for building user interfaces",
5 | "main": "ReactFiberReconciler.js",
6 | "repository": "http://github.com/iamdustan/tiny-react-renderer",
7 | "license": "BSD-3-Clause"
8 | }
9 |
--------------------------------------------------------------------------------
/src/fiber/.flowconfig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamdustan/tiny-react-renderer/2337d6558f0206e2b30c38e5df980bb70a046a92/src/fiber/.flowconfig
--------------------------------------------------------------------------------
/src/fiber/HowDoesFiberWork.md:
--------------------------------------------------------------------------------
1 | ## How Does Fiber Work?
2 |
3 | > Note: it is highly recommended that you read [https://github.com/acdlite/react-fiber-architecture](https://github.com/acdlite/react-fiber-architecture)
4 | > along with this. Andrew has a lot more foundational definitions and describes
5 | > the `fiber` data structure, whereas this describes how updates are scheduled
6 | > and commited from the renderers point of view.
7 |
8 | The fiber reconciler builds on the idea of Algebraic Effects. A custom renderer
9 | coordinates with the reconciler by informing it when certain effects should be
10 | scheduled. The type of effects are:
11 |
12 | * `NoEffect`
13 | * `Placement`
14 | * `Update`
15 | * `PlacementAndUpdate`
16 | * `Deletion`
17 | * `ContentReset`
18 | * `Callback`
19 | * `Err`
20 | * `Ref`
21 |
22 | This is likely best explained with an example using the DOM renderer. We’ll
23 | create a Composite Component that renders a text instance with the `"Hello"`
24 | inside of a `div` host instance. We’ll immediately render the `App` composite
25 | component updating the text instance contents to be the string `"World"`.
26 |
27 | ```jsx
28 | const App = (props) => {props.children}
;
29 |
30 | ReactDOM.render(Hello, document.body);
31 | ReactDOM.render(Goodbye, document.body);
32 | ```
33 |
34 | In the initial rendering Fiber will schedule `PlacementAndUpdate` effects to
35 | create the simple ` -> [text#Hello]` tree. When reconciling the second
36 | render call, when beginning work Fiber will schedule a `ContentReset` effect to
37 | clear the text of the `div` and an `Update` effect to schedule setting the new
38 | text content.
39 |
40 | A renderer informs a fiber reconciler what text effects to schedule and how to
41 | complete them through the following handful of methods:
42 |
43 | * `shouldSetTextContent`: communicates to the reconciler if a `ContentReset`
44 | effect should be scheduled
45 | * `resetTextContent`: Fiber calls this method when commiting the `ContentReset` effect
46 | * `createTextInstance`: Fiber calls this method when it needs a new host text instance
47 | * `commitTextUpdate`: Fiber calls this method to commit the new text content `Update` effect
48 |
49 |
--------------------------------------------------------------------------------
/src/fiber/README.md:
--------------------------------------------------------------------------------
1 | # Tiny React Renderer
2 |
3 | > Note that this is currently targeting the **React 16.0.0-alpha.3** release.
4 |
5 | Creating a fiber-based React renderer is quite direct, though there are a few
6 | awkward pieces around tooling that will be smoothed over in time.
7 |
8 | This guide can be read by jumping straight into the code to see the minimal work
9 | to implement a renderer, or you can read the [./HowDoesFiberWork.md](./HowDoesFiberWork.md)
10 | document for additional information on *how* Fiber works.
11 |
12 | With Fiber, all renderers begin (and maybe even end) in the React{Host}Fiber.js
13 | file.
14 |
15 | With that let’s get started in [./ReactTinyFiber.js](./ReactTinyFiber.js)!
16 |
17 | ## Work in Progress
18 |
19 | Please note this guide is a work in progress. Much of this knowledge is derived
20 | from my experience in creating [React Hardware](https://github.com/iamdustan/react-hardware).
21 |
22 |
--------------------------------------------------------------------------------
/src/fiber/ReactTinyFiber.js:
--------------------------------------------------------------------------------
1 | /***
2 | * Welcome to the Tiny React Renderer on Fiber.
3 | *
4 | * The Reconciler API for the current targeted revision is available at:
5 | * https://github.com/facebook/react/blob/ca4325e3eff16b86879188eb996ebcc9a933336a/src/renderers/shared/fiber/ReactFiberReconciler.js#L48-L104
6 | *
7 | * A renderer is the public interface to a React reconciler. With Fiber you
8 | * create a reconciler by calling `ReactFiberReconciler` with a `HostConfig`
9 | * object.
10 | *
11 | * All types for creating a react reconciler are manually extracted into
12 | * `../react-types` for the current revision (16.0.0-alpha.3).
13 | *
14 | * @flow
15 | */
16 |
17 | 'use strict';
18 |
19 | /**
20 | * The two internal types you need to be aware of. Everything else should be
21 | * kept private except host-specific things that you handle in your renderer.
22 | */
23 | import type { HostConfig, Reconciler } from 'react-fiber-types';
24 | import type { ReactNodeList } from 'react-fiber-types/ReactTypes';
25 |
26 | // our renconciler types are defined in ./ReactTinyTypes.js for a convenient place to see
27 | // what types you’re expected to define when implementing a renderer
28 | import type {
29 | Props,
30 | Container,
31 | Instance,
32 | TextInstance,
33 | OpaqueHandle,
34 | HostContext,
35 | } from './ReactTinyTypes';
36 |
37 | /**
38 | * This is the only entry point you need to create a Fiber renderer. Note that
39 | * it currently lives within the `react-dom` package and not `react.
40 | */
41 | const ReactFiberReconciler : (
42 | hostConfig: HostConfig<*, *, *, *, *, *, *, *>
43 | ) => Reconciler<*, *, *> = require('react-dom/lib/ReactFiberReconciler');
44 |
45 | const LOG_STEPS = false;
46 | const log = (a, b, c) => {
47 | if (LOG_STEPS) {
48 | console.log(a, b, c);
49 | }
50 | };
51 |
52 | const toJSON = (node) => {
53 | const props = node.props;
54 | if (typeof props.toJSON === 'function') {
55 | return props.toJSON(props);
56 | }
57 |
58 | let children = null;
59 | if (props.children) {
60 | if (Array.isArray(props.children)) {
61 | children = props.children.map(toJSON);
62 | } else if (props.children) {
63 | children = toJSON(props.children);
64 | }
65 | return Object.assign({}, props, {children});
66 | } else {
67 | const clone = Object.assign({}, props);
68 | delete clone.children;
69 | return clone;
70 | }
71 | };
72 |
73 |
74 | /**
75 | * The fun begins!
76 | *
77 | * We create a private reconciler instance. The methods defined here can be
78 | * thought of as the lifecycle of a renderer. React will manage all non-host
79 | * components, such as composites, stateless, and fragments.
80 | */
81 | const TinyRenderer = ReactFiberReconciler({
82 |
83 | // the tree creation and updating methods. If you’re familiar with the DOM API
84 | // this will look familiar
85 |
86 | createInstance(
87 | type : string,
88 | props : Props,
89 | rootContainerInstance : Container,
90 | hostContext : HostContext,
91 | internalInstanceHandle : Object
92 | ) {
93 | if (props.toJSON) {
94 | return props.toJSON(props);
95 | } else {
96 | return toJSON({props});
97 | }
98 | },
99 |
100 | // this is called instead of `appendChild` when the parentInstance is first
101 | // being created and mounted
102 | // added in https://github.com/facebook/react/pull/8400/
103 | appendInitialChild(
104 | parentInstance : Instance,
105 | child : Instance | TextInstance
106 | ) : void {
107 | //
108 | log('appendInitialChild', child);
109 | },
110 |
111 |
112 | appendChild(
113 | parentInstance : Instance | Container,
114 | child : Instance | TextInstance
115 | ) : void {
116 | log('appendChild', child);
117 | // const index = parentInstance.children.indexOf(child);
118 | // if (index !== -1) {
119 | // parentInstance.children.splice(index, 1);
120 | // }
121 | // parentInstance.children.push(child);
122 | },
123 |
124 | removeChild(
125 | parentInstance : Instance | Container,
126 | child : Instance | TextInstance
127 | ) : void {
128 | log('removeChild', child);
129 | // parentInstance.removeChild(child);
130 | },
131 |
132 | insertBefore(
133 | parentInstance : Instance | Container,
134 | child : Instance | TextInstance,
135 | beforeChild : Instance | TextInstance
136 | ) : void {
137 | log('insertBefore');
138 | // parentInstance.insertBefore(child, beforeChild);
139 | },
140 |
141 | // finalizeInitialChildren is the final HostConfig method called before
142 | // flushing the root component to the host environment
143 |
144 | finalizeInitialChildren(
145 | instance : Instance,
146 | type : string,
147 | props : Props,
148 | rootContainerInstance : Container
149 | ) : boolean {
150 | log('finalizeInitialChildren');
151 | // setInitialProperties(instance, type, props, rootContainerInstance);
152 | return false;
153 | },
154 |
155 | // prepare update is where you compute the diff for an instance. This is done
156 | // here to separate computation of the diff to the applying of the diff. Fiber
157 | // can reuse this work even if it pauses or aborts rendering a subset of the
158 | // tree.
159 |
160 | prepareUpdate(
161 | instance : Instance,
162 | type : string,
163 | oldProps : Props,
164 | newProps : Props,
165 | rootContainerInstance : Container,
166 | hostContext : HostContext
167 | ) : null | Array {
168 | log('TODO: prepareUpdate');
169 | return null;
170 | // return diffProperties(instance, type, oldProps, newProps, rootContainerInstance, hostContext);
171 | },
172 |
173 | commitUpdate(
174 | instance : Instance,
175 | updatePayload : Array,
176 | type : string,
177 | oldProps : Props,
178 | newProps : Props,
179 | internalInstanceHandle : Object,
180 | ) : void {
181 | // Apply the diff to the DOM node.
182 | // updateProperties(instance, updatePayload, type, oldProps, newProps);
183 | log('TODO: updateProperties');
184 | },
185 |
186 | // commitMount is called after initializeFinalChildren *if*
187 | // `initializeFinalChildren` returns true.
188 |
189 | commitMount(
190 | instance : Instance,
191 | type : string,
192 | newProps : Props,
193 | internalInstanceHandle : Object
194 | ) {
195 | log('commitMount');
196 | // noop
197 | },
198 |
199 | // HostContext is an internal object or reference for any bookkeeping your
200 | // renderer may need to do based on current location in the tree. In DOM this
201 | // is necessary for calling the correct `document.createElement` calls based
202 | // upon being in an `html`, `svg`, `mathml`, or other context of the tree.
203 |
204 | getRootHostContext(rootContainerInstance : Container) : HostContext {
205 | log('getRootHostContext');
206 | return emptyObject;
207 | },
208 |
209 | getChildHostContext(parentHostContext : HostContext, type: string) : HostContext {
210 | log('getChildHostContext');
211 | return emptyObject;
212 | },
213 |
214 | // getPublicInstance should be the identity function in 99% of all scenarios.
215 | // It was added to support the `getNodeMock` functionality for the
216 | // TestRenderers.
217 |
218 | getPublicInstance(instance : Instance | TextInstance) {
219 | log('getPublicInstance');
220 | if (instance == null) {
221 | return null;
222 | }
223 | console.log(instance)
224 | return instance != null && instance.props.toJSON(instance);
225 | },
226 |
227 | // the prepareForCommit and resetAfterCommit methods are necessary for any
228 | // global side-effects you need to trigger in the host environment. In
229 | // ReactDOM this does things like disable the ReactDOM events to ensure no
230 | // callbacks are fired during DOM manipulations
231 |
232 | prepareForCommit() : void {
233 | log('prepareForCommit');
234 | // noop
235 | },
236 |
237 | resetAfterCommit() : void {
238 | log('resetAfterCommit');
239 | // noop
240 | },
241 |
242 | // the following four methods are regarding TextInstances. In our example
243 | // renderer we don’t have specific text nodes like the DOM does so we’ll just
244 | // noop all of them.
245 |
246 | shouldSetTextContent(props : Props): boolean {
247 | log('shouldSetTextContent');
248 | return false
249 | },
250 |
251 | resetTextContent(instance : Instance) : void {
252 | log('resetTextContent');
253 | // noop
254 | },
255 |
256 | createTextInstance(
257 | text : string,
258 | rootContainerInstance : Container,
259 | hostContext : HostContext,
260 | internalInstanceHandle : OpaqueHandle
261 | ) : TextInstance {
262 | log('createTextInstance');
263 | return null;
264 | },
265 |
266 | commitTextUpdate(
267 | textInstance : TextInstance,
268 | oldText : string,
269 | newText : string
270 | ) : void {
271 | log('commitTextUpdate');
272 | // noop
273 | throw new Error('commitTextUpdate should not be called');
274 | },
275 |
276 | scheduleAnimationCallback() {
277 | log('scheduleAnimationCallback');
278 | },
279 |
280 | scheduleDeferredCallback() {
281 | log('scheduleDeferredCallback');
282 | },
283 |
284 | useSyncScheduling: true,
285 | });
286 |
287 | /**
288 | * Our public renderer. When someone requires your renderer, this is all they
289 | * should have access to. `render` and `unmountComponentAtNode` methods should
290 | * be considered required, though that isn’t strictly true.
291 | */
292 | const defaultContainer = {};
293 | const Tiny = {
294 | render(
295 | element : React$Element,
296 | callback : ?Function,
297 | container : any,
298 | ) {
299 | const containerKey = typeof container === 'undefined' ? defaultContainer : container;
300 | let root = roots.get(containerKey);
301 | if (!root) {
302 | root = TinyRenderer.createContainer(containerKey);
303 | roots.set(container, root);
304 | }
305 |
306 | TinyRenderer.updateContainer((element : any), root, null, callback);
307 | return TinyRenderer.getPublicRootInstance(root);
308 | },
309 | unmountComponentAtNode(container : any) {
310 | const containerKey = typeof container === 'undefined' ? defaultContainer : container;
311 | const root = roots.get(containerKey);
312 | if (root) {
313 | TinyRenderer.updateContainer(null, root, null, () => {
314 | roots.delete(container);
315 | });
316 | }
317 | },
318 | // other API methods you may support, such as `renderPortal()`
319 | };
320 |
321 | const roots = new Map();
322 | const emptyObject = {};
323 |
324 | module.exports = Tiny;
325 |
326 |
--------------------------------------------------------------------------------
/src/fiber/ReactTinyTypes.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | export type Props = {
4 | children : null | Instance | Array,
5 | toJSON ?: Function
6 | };
7 | export type Container = {};
8 | export type Instance = {
9 | type: string | Function,
10 | props: Props,
11 | };
12 | export type TextInstance = null;
13 | export type OpaqueHandle = Object;
14 | export type HostContext = Object;
15 |
16 |
--------------------------------------------------------------------------------
/src/fiber/index.js:
--------------------------------------------------------------------------------
1 | /** @flow */
2 |
3 | module.exports = require('./ReactTinyFiber');
4 |
5 |
--------------------------------------------------------------------------------
/src/fiber/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tiny-react-renderer-fiber",
3 | "private": true,
4 | "description": "A tiny React fiber renderer to demonstrate how to write a renderer.",
5 | "main": "./index.js",
6 | "scripts": {
7 | "test": "pushd ../../; npm run test-fiber"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/iamdustan/tiny-react-renderer"
12 | },
13 | "files": [
14 | "*",
15 | "package.json",
16 | "README.md"
17 | ],
18 | "keywords": [
19 | "react",
20 | "reactjs",
21 | "renderer"
22 | ],
23 | "author": "Dustan Kasten ",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/iamdustan/tiny-render-renderer/issues"
27 | },
28 | "homepage": "https://github.com/iamdustan/tiny-render-renderer",
29 | "dependencies": {
30 | "fbjs": "^0.8.4",
31 | "react": "16.0.0-alpha.3",
32 | "react-dom": "16.0.0-alpha.3",
33 | "react-fiber-types": "file:../fiber-types"
34 | },
35 | "devDependencies": {}
36 | }
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/fiber/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | asap@~2.0.3:
6 | version "2.0.5"
7 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
8 |
9 | core-js@^1.0.0:
10 | version "1.2.7"
11 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
12 |
13 | encoding@^0.1.11:
14 | version "0.1.12"
15 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
16 | dependencies:
17 | iconv-lite "~0.4.13"
18 |
19 | fbjs@^0.8.4, fbjs@^0.8.9:
20 | version "0.8.9"
21 | resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.9.tgz#180247fbd347dcc9004517b904f865400a0c8f14"
22 | dependencies:
23 | core-js "^1.0.0"
24 | isomorphic-fetch "^2.1.1"
25 | loose-envify "^1.0.0"
26 | object-assign "^4.1.0"
27 | promise "^7.1.1"
28 | setimmediate "^1.0.5"
29 | ua-parser-js "^0.7.9"
30 |
31 | iconv-lite@~0.4.13:
32 | version "0.4.15"
33 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
34 |
35 | is-stream@^1.0.1:
36 | version "1.1.0"
37 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
38 |
39 | isomorphic-fetch@^2.1.1:
40 | version "2.2.1"
41 | resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
42 | dependencies:
43 | node-fetch "^1.0.1"
44 | whatwg-fetch ">=0.10.0"
45 |
46 | js-tokens@^3.0.0:
47 | version "3.0.1"
48 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
49 |
50 | loose-envify@^1.0.0, loose-envify@^1.1.0:
51 | version "1.3.1"
52 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
53 | dependencies:
54 | js-tokens "^3.0.0"
55 |
56 | node-fetch@^1.0.1:
57 | version "1.6.3"
58 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
59 | dependencies:
60 | encoding "^0.1.11"
61 | is-stream "^1.0.1"
62 |
63 | object-assign@^4.1.0:
64 | version "4.1.1"
65 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
66 |
67 | promise@^7.1.1:
68 | version "7.1.1"
69 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf"
70 | dependencies:
71 | asap "~2.0.3"
72 |
73 | react-dom@16.0.0-alpha.3:
74 | version "16.0.0-alpha.3"
75 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.0.0-alpha.3.tgz#9cf304ea4bcdafe026f29ec2e71373316ec597ab"
76 | dependencies:
77 | fbjs "^0.8.9"
78 | loose-envify "^1.1.0"
79 | object-assign "^4.1.0"
80 |
81 | "react-fiber-types@file:../fiber-types":
82 | version "16.0.0-alpha.3"
83 |
84 | react@16.0.0-alpha.3:
85 | version "16.0.0-alpha.3"
86 | resolved "https://registry.yarnpkg.com/react/-/react-16.0.0-alpha.3.tgz#addfd7ae9d801fd20c6244142354ae0cb7b1fe00"
87 | dependencies:
88 | fbjs "^0.8.9"
89 | loose-envify "^1.1.0"
90 | object-assign "^4.1.0"
91 |
92 | setimmediate@^1.0.5:
93 | version "1.0.5"
94 | resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
95 |
96 | ua-parser-js@^0.7.9:
97 | version "0.7.12"
98 | resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
99 |
100 | whatwg-fetch@>=0.10.0:
101 | version "2.0.2"
102 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e"
103 |
--------------------------------------------------------------------------------
/src/stack/README.md:
--------------------------------------------------------------------------------
1 | # Tiny React Renderer
2 |
3 | Creating a stack-based renderer is a fairly straight-forward affair once you
4 | know what you’re looking for.
5 |
6 | Many languages have this concept of a `main`—the entry point to your
7 | application. If you look at any React application code you’ve written you’ll see
8 | that you “start” your app with a call like the following:
9 |
10 | ```jsx
11 | // web
12 | ReactDOM.render(React.createElement(MyApp), document.getElementById('app'));
13 |
14 | // native
15 | AppRegistry.registerComponent('MyApp', () => MyApp);
16 | ```
17 |
18 | This is where your application enters into the React domain and comes alive. Your
19 | root React element is instantiated and attached to the host environment.
20 |
21 | If you follow either the ReactDOM or React Native codebases from where these
22 | methods are defined you will quickly find yourself at the `React{Host}Mount.js`
23 | file. Our renderer also begins there.
24 |
25 | With that let’s get started! Our tour continues in [./mount.js](./mount.js).
26 |
27 | ## Work in Progress
28 |
29 | Please note this guide is a work in progress. Much of this knowledge is derived
30 | from my experience in creating [React Hardware](https://github.com/iamdustan/react-hardware).
31 |
32 | ## Thanks
33 |
34 | * [@thejameskyle](https://github.com/thejameskyle): for the inspiration of repo style
35 | * [@ryanflorence](https://github.com/ryanflorence) and [@mjackson](https://github.com/mjackson) for React Router and the problem that inspired this
36 | * [@gaearon](https://github.com/gaearon), [@matthewwithanm](https://github.com/matthewwithanm),
37 | [@vjeux](https://github.com/vjeux), [@zpao](https://github.com/zpao),
38 | [@Yomguithereal](https://github.com/Yomguithereal), [@axemclion](https://github.com/axemclion),
39 | and everyone else who has helped me poke around the React codebase.
40 |
--------------------------------------------------------------------------------
/src/stack/component.js:
--------------------------------------------------------------------------------
1 | /***
2 | * Welcome to the Tiny React Renderer.
3 | *
4 | * You should read this guide in the following order:
5 | *
6 | * 1. mount.js
7 | * 2. injection.js
8 | * 3. component.js
9 | * 4. Any of the appendices you find interesting and the many React renderer
10 | * source files.
11 | */
12 |
13 | /**
14 | * React Components are the heart and soul of a React renderer. This is the
15 | * internal side of a consumer's ReactComponents. At a high level, you will
16 | * define one or more internal components to map to the public interface of your
17 | * renderer. Below this we have defined two ReactComponents.
18 | *
19 | * * MinimumViableComponent
20 | * * TinyRendererComponent
21 | *
22 | * MinimumViableComponent is an overview of the minimum interface required to be
23 | * a valid ReactComponent.
24 | *
25 | * TinyRendererComponent is the component that is constructed in the Tiny React
26 | * Renderer.
27 | */
28 | 'use strict';
29 |
30 | const ReactMultiChild = require('react/lib/ReactMultiChild');
31 | const serialize = require('./utilities/serialize');
32 |
33 | /**
34 | * This MinimumViableComponent is the minimal interface to be recognized as a
35 | * valid React Component with support for rendering children. This is assuming
36 | * that the default case for a component is to support children.
37 | *
38 | * This has a very symmetrical relationship to the userland side of creating a
39 | * React Component. In addition, there are private methods used by other parts
40 | * of React Core, such as `getPublicInstance` or
41 | * React <= 15.0 `getNativeNode`
42 | * React > 15.0 `getHostNode`
43 | */
44 | const MinimumViableComponent = function(element) {
45 | // This internal API—while relatively unchanged for a while—is likely pretty
46 | // volatile. Many of these names began with ReactDOM and ReactART which may
47 | // not be the *best* abstraction for them.
48 |
49 | // `this.node` in ReactDOM points to the DOM node of a component.
50 | // `getNativeNode`/`getHostNode` should return this.
51 | this.node = null;
52 | // `this._mountImage` is the representation you use to render a ReactElement
53 | // hierarchy. This could be an HTML string, a DOM node, or an identifier or
54 | // simple representation that maps to the final result such as native UI
55 | // controls.
56 | this._mountImage = null;
57 | // `this._renderedChildren` is something in the form of null|Child|Array
58 | // `ReactMultiChild.Mixin` is primarily responsible for managing this property
59 | this._renderedChildren = null;
60 | // `this._currentElement` is the currently rendered ReactElement. This is
61 | // important because it allows you to compare the node and props on lifecycle
62 | // methods to update appropriately.
63 | this._currentElement = element;
64 | };
65 |
66 | MinimumViableComponent.prototype = Object.assign(
67 | {
68 | // A nice symmetry to [TODO: React Lifecycle Methods] exists here.
69 | // These are the required methods to implement. You may additionally provide
70 | // custom implementations of other lifecycle methods or any arbitrary
71 | // methods that are private to your implementation.
72 | getPublicInstance() {},
73 | mountComponent() {},
74 | receiveComponent() {},
75 | unmountComponent() {},
76 | // Implement both of these for now. React <= 15.0 uses getNativeNode, but
77 | // that is confusing. Host environment is more accurate and will be used
78 | // going forward
79 | getNativeNode() {},
80 | getHostNode() {}
81 | },
82 | ReactMultiChild.Mixin
83 | );
84 |
85 | /**
86 | * The fun begins! Well, it would in a not-so-tiny renderer, but for here it’s
87 | * pretty straight-forward. The idea for this particular implementation came
88 | * from conversations with Ryan Florence and Michael Jackson from the React
89 | * Router/React.js Training teams.
90 | *
91 | * React Router supports two route configuration approaches:
92 | *
93 | * 1. ReactComponents `{...}`
94 | * 2. Object Literal `[{path: '/', component: C}, {path: '/list', component: L}]`
95 | *
96 | * As I understand it, both approaches result in the object literal, it’s just a
97 | * matter of the path to get there. The question then is what would it look
98 | * like to write a renderer that accomplishes the ReactComponent authoring style
99 | * in a more elegant way?
100 | *
101 | * If you recall from our mount.js render method, the return value or callback
102 | * is called with the result of `component.getPublicInstance()`. We want this to
103 | * be the JSON representation of our route configuration.
104 | *
105 | * Below we will provide a default serialize.toJSON implementation and the bare
106 | * minimum required to mount and update these ReactElements.
107 | */
108 | const TinyRendererComponent = function(element) {
109 | this.node = null;
110 | this._mountImage = null;
111 | this._renderedChildren = null;
112 | this._currentElement = element;
113 | };
114 |
115 | const TinyRendererComponentMixin = {
116 | getPublicInstance() {
117 | // serialize.toJSON is the default serialization provided.
118 | //
119 | // It simply returns the node.props of a component with serialized children.
120 | // It also looks at whether a component has a custom `toJSON` method and will
121 | // use that instead, allowing consumers to provide their own serialization and
122 | // impacting the resulting public instance.
123 |
124 | return serialize.toJSON(this.node)
125 | },
126 |
127 | mountComponent(
128 | transaction,
129 | nativeParent,
130 | nativeContainerInfo,
131 | context
132 | ) {
133 | // In a not-so-tiny renderer you would also want to validate the properties
134 | // (in dev mode) and apply them to the host environment.
135 | // I have often seen renderers have a `render` method defined on their
136 | // internal component implementation that is responsible for calling the
137 | // appropriate methods to update the UI. For example that could be DOM
138 | // methods, ReactNativeUIManager bridging calls, or node-blessed methods.
139 | this.node = this._currentElement;
140 | this.mountChildren(this.node.children, transaction, context);
141 |
142 | return this.node;
143 | },
144 |
145 | receiveComponent(nextElement, transaction, context) {
146 | // Typically you would diff the props and apply those to the host
147 | // environment, though all we need to do is swap out our _currentElement.
148 | const prevElement = this._currentElement;
149 | this._currentElement = nextElement;
150 |
151 | // this.updateChildren comes from ReactMultiChild.Mixin
152 | this.updateChildren(nextElement.props.children, transaction, context);
153 | },
154 | // there is no native node
155 | getHostNode() {},
156 | // how do you unmount JSON?
157 | unmountComponent() {},
158 | };
159 |
160 | Object.assign(
161 | TinyRendererComponent.prototype,
162 | TinyRendererComponentMixin,
163 | ReactMultiChild.Mixin
164 | );
165 |
166 | /**
167 | * CONGRATULATIONS! You now understand the basics of how a React Renderer works.
168 | * While there are still a lot of React internals we didn’t look at, you saw the
169 | * foundational pieces. Most every renderer listed on
170 | * http://iamdustan.com/react-renderers has the same entry path and hooks as
171 | * what you just learned.
172 | *
173 | * A lot of ReactDOM and ReactNative’s source is integrating the concepts we
174 | * just learned in this binding layer to a host environment. From this
175 | * foundation you have a solid understanding of the glue. ReactCore deals with
176 | * details of reconciliation and transactions, while renderers are plumbing.
177 | *
178 | * Fin.
179 | */
180 | module.exports = TinyRendererComponent;
181 |
182 |
--------------------------------------------------------------------------------
/src/stack/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const render = require('./mount');
4 |
5 | module.exports = {render};
6 |
7 |
--------------------------------------------------------------------------------
/src/stack/injection.js:
--------------------------------------------------------------------------------
1 | /***
2 | * Welcome to the Tiny React Renderer.
3 | *
4 | * You should read this guide in the following order:
5 | *
6 | * 1. mount.js
7 | * 2. injection.js
8 | * 3. component.js
9 | * 4. Any of the appendices you find interesting and the many React renderer
10 | * source files.
11 | */
12 |
13 | /**
14 | * A React renderer is responsible for injecting the implementations for one or
15 | * more internal concepts.
16 | *
17 | * The following list of 28 files is the result of searching for `inject` in the
18 | * React source code as of React 15.0.0. Since these are private APIs this is
19 | * not intended to be a dictionary of all possible injection points, but to
20 | * provide a reference of areas that may have the possibility to be customized.
21 | *
22 | * * BeforeInputEventPlugin.js
23 | * * DefaultEventPluginOrder.js
24 | * * dangerousStyleValue.js
25 | * * DOMProperty.js
26 | * * EventPluginHub.js
27 | * * EventPluginRegistry.js
28 | * * EventPluginUtils.js
29 | * * HTMLDOMPropertyConfig.js
30 | * * ReactBrowserEventEmitter.js
31 | * * ReactComponentBrowserEnvironment.js
32 | * * ReactComponent.js
33 | * * ReactComponentEnvironment.js
34 | * * ReactClass.js
35 | * * ReactDOM.js
36 | * * ReactDefaultInjection.js
37 | * * ReactDefaultPerf.js
38 | * * ReactDOMServer.js
39 | * * ReactDOMComponent.js
40 | * * ReactEmptyComponent.js
41 | * * ReactInjection.js
42 | * * ReactHostComponent.js
43 | * * ReactPerf.js
44 | * * ReactMount.js
45 | * * ReactServerRendering.js
46 | * * ReactTestUtils.js
47 | * * ReactUpdates.js
48 | * * ResponderEventPlugin.js
49 | * * Transaction.js
50 | */
51 | 'use strict';
52 |
53 | /**
54 | * ReactInjection is likely the most stable location to look for injection
55 | * points. As of 15.0.0 this contains the following 10 modules:
56 | *
57 | * * Component
58 | * * Class
59 | * * DOMProperty
60 | * * EmptyComponent
61 | * * EventPluginHub
62 | * * EventPluginUtils
63 | * * EventEmitter
64 | * * HostComponent
65 | * * Perf
66 | * * Updates
67 | *
68 | * Some of these can be injected verbatim for most scenarios, and we provide our
69 | * component implementation(s) to be called at the appropriate times.
70 | */
71 | const ReactInjection = require('react/lib/ReactInjection');
72 | const ReactDefaultBatchingStrategy = require('react/lib/ReactDefaultBatchingStrategy');
73 | const TinyRendererReconcileTransaction = require('./reconcileTransaction');
74 | const TinyRendererComponent = require('./component');
75 |
76 | function inject() {
77 | // For the Tiny React Renderer only the generic component class will be
78 | // injected. The HostComponent injection has the following three methods:
79 | //
80 | // * injectGenericComponentClass
81 | // * injectTextComponentClass
82 | //
83 | // `GenericComponentClass` is analogous to $JSXInstrinsics in flow
84 | // terminology. This is a lowercase JSXElement such as
85 | //
86 | // `TextComponentClass` is what class should be used when text is being
87 | // rendered. This is what would be called for the single child in
88 | // Hello, world.
89 | (ReactInjection.NativeComponent || ReactInjection.HostComponent).injectGenericComponentClass(
90 | TinyRendererComponent
91 | );
92 |
93 | ReactInjection.Updates.injectReconcileTransaction(
94 | TinyRendererReconcileTransaction
95 | );
96 |
97 | ReactInjection.Updates.injectBatchingStrategy(
98 | ReactDefaultBatchingStrategy
99 | );
100 | }
101 |
102 | module.exports = {inject};
103 |
104 |
--------------------------------------------------------------------------------
/src/stack/mount.js:
--------------------------------------------------------------------------------
1 | /***
2 | * Welcome to the Tiny React Renderer.
3 | *
4 | * You should read this guide in the following order:
5 | *
6 | * 1. mount.js
7 | * 2. injection.js
8 | * 3. component.js
9 | * 4. Any of the appendices you find interesting and the many React renderer
10 | * source files.
11 | */
12 |
13 | /**
14 | * This file has been authored in a linear manner and is not reflective of many
15 | * of the steps you would take while writing a production renderer.
16 | *
17 | * We’re going to look at the four required modules that a React renderer has.
18 | * Beyond this you will have the work that binds a renderer implementation to a
19 | * host environment. For this Tiny React Renderer we’re implementing a glorified
20 | * `toJSON` renderer and will not have that. See
21 | * [Appendix 3: Host Environment Examples](https://github.com/iamdustan/tiny-react-renderer/tree/master/appendix/3-Host-Environment-Examples.md)
22 | * to learn more.
23 | *
24 | * The four core modules in a React renderer are:
25 | *
26 | * * DefaultInjection
27 | * * Mount
28 | * * Component
29 | * * ReconcileTransaction
30 | *
31 | * `DefaultInjection` must inject its implementations before any other work can
32 | * be done. It is part of a renderer's initialization process.
33 | *
34 | * `Mount` can be thought of a renderer's `main` method. All userland code enters
35 | * the React world through this door.
36 | *
37 | * `ReconcileTransaction` is a class which maintains the logic for how
38 | * reconciliations take place. Many renderers can use the simple transaction as
39 | * provided in here, although a lot of complexity can be contained within here.
40 | * See [Appendix 2: Reconcile Transaction](https://github.com/iamdustan/tiny-react-renderer/tree/master/appendix/2-Reconcile-Transaction.md)
41 | * for a deeper look into the default DOM ReactReconcileTransaction details.
42 | *
43 | * `Component` is the internal class to a consumer's component. This is where
44 | * property diffing is computed and applied and where the realization of a
45 | * renderer takes place.
46 | */
47 |
48 | /**
49 | * As with most modern JavaScript projects in 2016, we begin by importing our
50 | * dependencies. The first dependency here will typically be seen inline, but
51 | * has been moved out for clarity while reading the primary `mount` method
52 | * below.
53 | *
54 | * After this we require a few React internal modules that allow us to
55 | * instantiate ReactComponents and integrate with the reconciliation algorithm.
56 | *
57 | * The final require introduces us to a common pattern in React core which is
58 | * the ability to `inject` an implementation into different parts of the core
59 | * application. For example, ReactDOM injects a generic component class that
60 | * handles rendering , , and , and any other DOM node.
61 | *
62 | * The `./injection.js` file documents this in greater detail.
63 | */
64 | 'use strict';
65 |
66 | const invariants = require('./utilities/invariants');
67 | const instantiateReactComponent = require('react/lib/instantiateReactComponent');
68 | const ReactInstanceHandles = require('react/lib/ReactInstanceHandles');
69 | const ReactUpdates = require('react/lib/ReactUpdates');
70 | const DefaultInjection = require('./injection');
71 |
72 | /**
73 | * Step 0. Inject the unique aspects of your renderer into React core
74 | * immediately. These will be things like your event system, generic component
75 | * handler, reconciliation strategy, or batching strategy.
76 | */
77 | DefaultInjection.inject();
78 |
79 | /**
80 | * Step 1. Userlands entry into the React world. Regardless of the renderer you
81 | * use—whether ReactDOM, ReactNative, ReactHardware, ReactBlessed, or one of the
82 | * many others—all `ReactDOM.render|RN.registerComponent|ReactBlessed.render`
83 | * type of methods will enter the React world through this method.
84 | *
85 | * As an example:
86 | * ```
87 | * ReactDOM.render(
88 | * Hello, World
,
89 | * document.getElementById('app'),
90 | * (inst) => console.log(inst)
91 | * );
92 | * ```
93 | *
94 | * will reach this method in the `ReactMount` module. Your signature should have
95 | * the `ReactElement` as the first argument, an optional callback as the last
96 | * argument, and any environment-specific arguments in between.
97 | *
98 | * Optionally, you may return the public instance of the rendered component as
99 | * the return value.
100 | **/
101 | const render = (
102 | nextElement, // ReactElement description.
103 | callback // optional callback for when mount is complete
104 | ) => {
105 |
106 | // The first thing you'll want to do in here is confirm the caller passed in a
107 | // valid ReactElement. The implementation of this is the same across renderers
108 | // with the exception of the error message through when the invariant fails.
109 | invariants.isValidElement(nextElement);
110 |
111 | // For this tiny renderer, we do not have a target element to render into,
112 | // though many renderers have this concern. In this scenario you should
113 | // consider applying a `warning` or `invariant` to that argument to ensure the
114 | // consumer has an educational experience in development mode. A key objective
115 | // of writing a renderer is to make interacting with the host system simpler.
116 | // A given renderer should seek to help its users to avoid simple mistakes.
117 | // such as passing in a non-existent DOM node.
118 | //
119 | // @example:
120 | // warning.isValidTargetElement(targetElement);
121 |
122 | // Appendix 1: Rerendering A Top Level Element
123 | // https://github.com/iamdustan/tiny-react-renderer/tree/master/appendix/1-Rendering-A-Top-Level-Element.md
124 | //
125 | // If there is a target element or the opportunity to reuse a previous render
126 | // call, you would look up the previous element and reconcile from there.
127 |
128 | // Woohoo! The consumer has now made it to the point where we’re interacting
129 | // with React internals! Since any application can have multiple roots, we
130 | // want to get an identifier from the `ReactInstanceHandles` component.
131 | //
132 | // Next we instantiate a new ReactComponent from the ReactElement passed in.
133 | // See [React glossary](https://facebook.github.io/react/docs/glossary.html)
134 | // for more context on the relationship between ReactComponent and ReactElement.
135 | const rootId = ReactInstanceHandles.createReactRootID(0);
136 | const component = instantiateReactComponent(nextElement);
137 |
138 | // The initial render is currently synchronous, but any updates that happen
139 | // during rendering, in componentWillMount or componentDidMount, will be
140 | // batched according to the current batching strategy.
141 | //
142 | // Note: there is ongoing research for creating an incremental reconciler
143 | // which may impact this aspect of renderer creation.
144 | //
145 | // Assuming you’ve read the [React Reconciliation Algorithm](https://facebook.github.io/react/docs/reconciliation.html) article on
146 | // the React docs, this may be familiar. The “public” API for accomplishing
147 | // this is done with a batching strategy and transaction. React provides
148 | // default implementations for both of these and does not require any effort
149 | // from us other than calling them.
150 | ReactUpdates.batchedUpdates(() => {
151 | // Two points to React for using object pooling internally and being good
152 | // stewards of garbage collection and memory pressure.
153 | const transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
154 | transaction.perform(() => {
155 | // The `component` here is an instance of your
156 | // `ReactCustomRendererComponent` class. To be 100% honest, I’m not
157 | // certain if the method signature is enforced by React core or if it is
158 | // renderer specific. This is following the ReactDOM renderer. The
159 | // important piece is that we pass our transaction and rootId through, in
160 | // addition to any other contextual information needed.
161 | component.mountComponent(
162 | transaction,
163 | rootId,
164 | // TODO: what is _idCounter used for and when should it be nonzero?
165 | {_idCounter: 0},
166 | {}
167 | );
168 | if (callback) {
169 | callback(component.getPublicInstance());
170 | }
171 | });
172 | ReactUpdates.ReactReconcileTransaction.release(transaction);
173 |
174 | });
175 |
176 | return component.getPublicInstance();
177 | };
178 |
179 | // Congratulations! You’ve done it! You have a React renderer! Though so far
180 | // we haven’t done anything interesting. For that we need to implement our
181 | // ReactComponent class. For that you’ll head over to `./component.js`. See you
182 | // there!
183 | module.exports = render;
184 |
185 |
--------------------------------------------------------------------------------
/src/stack/package.js:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tiny-react-renderer-stack",
3 | "private": true,
4 | "description": "A tiny React stack renderer to demonstrate how to write a renderer.",
5 | "main": "./index.js",
6 | "scripts": {
7 | "test": "pushd ../../; node ./test --fiber"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/iamdustan/tiny-react-renderer"
12 | },
13 | "files": [
14 | "*",
15 | "package.json",
16 | "README.md"
17 | ],
18 | "keywords": [
19 | "react",
20 | "reactjs",
21 | "renderer"
22 | ],
23 | "author": "Dustan Kasten ",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/iamdustan/tiny-render-renderer/issues"
27 | },
28 | "homepage": "https://github.com/iamdustan/tiny-render-renderer",
29 | "dependencies": {
30 | "fbjs": "^0.8.4",
31 | "react": "15.3.x"
32 | },
33 | "devDependencies": {}
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/src/stack/reconcileTransaction.js:
--------------------------------------------------------------------------------
1 | /***
2 | * Welcome to the Tiny React Renderer.
3 | *
4 | * You should read this guide in the following order:
5 | *
6 | * 1. mount.js
7 | * 2. injection.js
8 | * 3. component.js
9 | * 4. Any of the appendices you find interesting and the many React renderer
10 | * source files.
11 | */
12 |
13 | /***
14 | * The ReconcileTransaction class provided here is the simplest scenario
15 | * possible. The source code here—including all callbacks beyond this point—is
16 | * the same as the ReactReconcileTransaction.js module in the React 15 source
17 | * code, but with the DOM event and selection handlers removed.
18 | *
19 | * Really interesting edge cases are managed from here, but nothing of
20 | * interest happens in a Tiny Renderer.
21 | */
22 |
23 | 'use strict';
24 |
25 | const CallbackQueue = require('react/lib/CallbackQueue');
26 | const PooledClass = require('react/lib/PooledClass');
27 | const Transaction = require('react/lib/Transaction');
28 |
29 | /**
30 | * Provides a `CallbackQueue` queue for collecting `onDOMReady` or analogous
31 | * callbacks during the performing of the transaction.
32 | */
33 | const ON_RENDERER_READY_QUEUEING = {
34 | /**
35 | * Initializes the internal firmata `connected` queue.
36 | */
37 | initialize: function() {
38 | this.reactMountReady.reset();
39 | },
40 |
41 | /**
42 | * After Hardware is connected, invoke all registered `ready` callbacks.
43 | */
44 | close: function() {
45 | this.reactMountReady.notifyAll();
46 | },
47 | };
48 |
49 | /**
50 | * Executed within the scope of the `Transaction` instance. Consider these as
51 | * being member methods, but with an implied ordering while being isolated from
52 | * each other.
53 | */
54 | const TRANSACTION_WRAPPERS = [ON_RENDERER_READY_QUEUEING];
55 |
56 | function TinyRendererReconcileTransaction() {
57 | this.reinitializeTransaction();
58 | this.reactMountReady = CallbackQueue.getPooled(null);
59 | }
60 |
61 | const Mixin = {
62 | /**
63 | * @see Transaction
64 | * @abstract
65 | * @final
66 | * @return {array