Skip to content

Commit

Permalink
- Implemented error boundary
Browse files Browse the repository at this point in the history
- Implenented portals
- Fixed refs
- Fixed unmountComponentAtNode
- Fix issue with work loop when loop resets in sync mode (due to suspense/error boundary) and the boundary is before top fiber.
-
  • Loading branch information
s-yadav committed Aug 2, 2020
1 parent 5d97f95 commit d496ef2
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 74 deletions.
30 changes: 17 additions & 13 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import RefsExample from './RefsExample';
import CreatePortalExample from './createPortalExample';
import SVGExample from './SVGExample';
import LazySuspenseExample from './lazySuspenseExample';
import ErrorBoundaryExample from './ErrorBoundaryExample';

function shuffle(array) {
array = [...array];
Expand All @@ -29,33 +30,37 @@ function shuffle(array) {
return array;
}

export function oldApp() {
export function OldApp() {
return (
<div>
<div className="wrapper">
{/* <div className="wrapper">
<h2>Todo List</h2>
<TodoList />
</div>
<div className="wrapper">
<h2>useState hook example</h2>
<UseStateExample />
</div>
<div className="wrapper">
</div> */}
{/* <div className="wrapper">
<h2>Context api example</h2>
<ContextExample />
</div>
<div className="wrapper">
</div> */}
{/* <div className="wrapper">
<h2>Refs example</h2>
<RefsExample />
</div>
<div className="wrapper">
</div> */}
{/* <div className="wrapper">
<h2>SVG Example</h2>
<SVGExample />
</div>
<div className="wrapper">
</div> */}
{/* <div className="wrapper">
<h2>Lazy and Suspense Example</h2>
<LazySuspenseExample />
</div>
</div> */}
{/* <div className="wrapper">
<h2>Error Boundary Example</h2>
<ErrorBoundaryExample />
</div> */}
{/** Keep the portal example on last */}
<div className="wrapper">
<h2>CreatePortal Example</h2>
Expand Down Expand Up @@ -112,8 +117,7 @@ export class AppBase extends Component {
export default function App() {
return (
<div>
<AppBase />
<AppBase />
<OldApp />
</div>
);
}
99 changes: 99 additions & 0 deletions example/ErrorBoundaryExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Brahmos from '../src';

class ErrorBoundary extends Brahmos.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}

static getDerivedStateFromError(error) {
return { error };
}

componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error: error,
errorInfo: errorInfo,
});
// You can also log error messages to an error reporting service here
}

render() {
if (this.state.error) {
// Error path
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo && this.state.errorInfo.componentStack}
</details>
</div>
);
}
// Normally, just render children
return this.props.children;
}
}

class BuggyCounter extends Brahmos.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(({ counter }) => ({
counter: counter + 1,
}));
}

render() {
if (this.state.counter === 5) {
// Simulate a JS error
throw new Error('I crashed!');
}
return <h1 onClick={this.handleClick}>{this.state.counter}</h1>;
}
}

export default function ErrorBoundaryExample() {
return (
<div>
<p>
<b>
This is an example of error boundaries in React 16.
<br />
<br />
Click on the numbers to increase the counters.
<br />
The counter is programmed to throw when it reaches 5. This simulates a JavaScript error in
a component.
</b>
</p>
<hr />
<ErrorBoundary>
<p>
These two counters are inside the same error boundary. If one crashes, the error boundary
will replace both of them.
</p>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
<hr />
<p>
These two counters are each inside of their own error boundary. So if one crashes, the other
is not affected.
</p>
<ErrorBoundary>
<BuggyCounter />
</ErrorBoundary>
<ErrorBoundary>
<BuggyCounter />
</ErrorBoundary>
</div>
);
}
10 changes: 5 additions & 5 deletions example/RefsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export default class RefsExample extends Component {
}

logRefs = () => {
// console.log(this.childCreateRef);
// console.log(this.childCallbackRef);
// console.log(this.domCreateRef);
// console.log(this.domCbRef);
// console.log(this.forwardedRef);
console.log(this.childCreateRef);
console.log(this.childCallbackRef);
console.log(this.domCreateRef);
console.log(this.domCbRef);
console.log(this.forwardedRef);
};

render() {
Expand Down
4 changes: 2 additions & 2 deletions example/createPortalExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ function CreatePortalExample() {
{display && createPortal(<Child />, document.querySelector('#another-root'))}
<button
onClick={() => {
setDisplay(false);
setDisplay(!display);
}}
>
Hide
Toggle
</button>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import ConcurrentApp from './concurrentApp';
import SuspenseApp from './suspenseExamples';
import SuspenseListApp from './suspenseListExample';

render(<ConcurrentApp />, document.getElementById('app'));
render(<App />, document.getElementById('app'));

// render(<UnMountAtNode/>, document.getElementById('unmount-node'));
render(<UnMountAtNode />, document.getElementById('unmount-node'));
2 changes: 1 addition & 1 deletion example/suspenseListExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function App() {

function ProfilePage({ resource }) {
return (
<SuspenseList revealOrder="forwards">
<SuspenseList revealOrder="forwards" tail="collapsed">
<Suspense fallback={<h1>Loading...</h1>}>
<ProfileDetails resource={resource} />
</Suspense>
Expand Down
4 changes: 2 additions & 2 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const defaultConfig = {
license({
banner,
}),
// terser(),
terser(),
],
};

Expand All @@ -55,7 +55,7 @@ const minConfig = {
format: 'umd',
name: 'Brahmos',
},
plugins: [...defaultConfig.plugins, terser()],
plugins: [...defaultConfig.plugins],
};

export default [defaultConfig, minConfig];
2 changes: 0 additions & 2 deletions src/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ export class Component {
if (callback) callback(this.state);
}

__handleError() {}

__render() {
// get the new rendered node
const nodes = this.render();
Expand Down
1 change: 1 addition & 0 deletions src/brahmosNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function brahmosNode(props, values, key) {
key,
added: false,
ref: null,
portalContainer: null,

/** Component specific properties */
type: null,
Expand Down
11 changes: 3 additions & 8 deletions src/createPortal.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import render from './render';

/**
* Render children outside the main DOM hierarchy of
* the parent component without losing the context
*/
function createPortal(child, container) {
// mark the child node as ported node
// TODO: Work on portal container logic with fiber
// child.portalContainer = container;

render(child, container);
// add portal container information in child
// which we can use for forming the part
if (child) child.portalContainer = container;

// We need to return the rendered child node so that life cycles are handled properly on render flow
return child;
}

Expand Down
12 changes: 11 additions & 1 deletion src/effectLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function handleComponentEffect(fiber) {
}

function handleComponentPostCommitEffect(fiber) {
const { node, nodeInstance, root } = fiber;
const { node, nodeInstance, root, childFiberError } = fiber;
const { updateType } = root;

const { nodeType, ref } = node;
Expand All @@ -177,6 +177,16 @@ function handleComponentPostCommitEffect(fiber) {
callLifeCycle(nodeInstance, 'componentDidUpdate', [prevProps, prevState, lastSnapshot]);
}

if (childFiberError) {
callLifeCycle(nodeInstance, 'componentDidCatch', [
childFiberError.error,
childFiberError.errorInfo,
]);

// reset the error
fiber.childFiberError = null;
}

// if the component node has ref call the ref with the node instance
if (ref) setRef(ref, nodeInstance);

Expand Down
17 changes: 6 additions & 11 deletions src/fiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ export function createHostFiber(domNode) {
}

export function createFiber(root, node, part) {
// if a node is ported node, update the part information
if (node && node.portalContainer) {
part.parentNode = node.portalContainer;
}

return {
node,
nodeInstance: null,
Expand All @@ -183,7 +188,7 @@ export function createFiber(root, node, part) {
part,
alternate: null, // points to the current fiber
context: null, // Points to the context applicable for that fiber
errorBoundary: null,
childFiberError: null,
isSvgPart: false,
deferredUpdateTime: 0,
updateTime: 0,
Expand Down Expand Up @@ -231,7 +236,6 @@ export function createAndLink(node, part, currentFiber, refFiber, parentFiber) {
fiber[updateTimeKey] = parentFiber[updateTimeKey];
fiber.context = parentFiber.context;
fiber.isSvgPart = parentFiber.isSvgPart;
fiber.errorBoundary = parentFiber.errorBoundary;

return fiber;
}
Expand Down Expand Up @@ -268,15 +272,6 @@ function getFiberWhichRequiresProcessing(fiber, lastCompleteTime, updateTimeKey)
}

export function getNextFiber(fiber, topFiber, lastCompleteTime, updateTimeKey) {
// if we have any retry fiber reset return that fiber
const { root } = fiber;
const { retryFiber } = root;
if (retryFiber) {
// reset the retry fiber and return it
root.retryFiber = null;
return retryFiber;
}

/**
* Skip fibers which does not require processing
*/
Expand Down
Loading

0 comments on commit d496ef2

Please sign in to comment.