Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
WIP Suspense and transition
- Fixed bugs
- Fixed circular dependencies
  • Loading branch information
s-yadav committed Jun 17, 2020
1 parent 8b8f047 commit 42267ed
Show file tree
Hide file tree
Showing 28 changed files with 1,008 additions and 494 deletions.
99 changes: 99 additions & 0 deletions example/fakeApi.js
@@ -0,0 +1,99 @@
export function fetchProfileData() {
const userPromise = fetchUser();
const postsPromise = fetchPosts();
const triviaPromise = fetchTrivia();
return {
user: wrapPromise(userPromise),
posts: wrapPromise(postsPromise),
trivia: wrapPromise(triviaPromise),
};
}

// Suspense integrations like Relay implement
// a contract like this to integrate with React.
// Real implementations can be significantly more complex.
// Don't copy-paste this into your project!
function wrapPromise(promise) {
let status = 'pending';
let result;
const suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
},
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
},
};
}

function fetchUser() {
console.log('fetch user...');
return new Promise((resolve) => {
setTimeout(() => {
console.log('fetched user');
resolve({
name: 'Ringo Starr',
});
}, 1000);
});
}

const ringoPosts = [
{
id: 0,
text: 'I get by with a little help from my friends',
},
{
id: 1,
text: "I'd like to be under the sea in an octupus's garden",
},
{
id: 2,
text: 'You got that sand all over your feet',
},
];

function fetchPosts() {
const ringoPostsAtTheTime = ringoPosts;
console.log('fetch posts...');
return new Promise((resolve) => {
setTimeout(() => {
console.log('fetched posts');
resolve(ringoPostsAtTheTime);
}, 1300);
});
}

function fetchTrivia() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{
id: 1,
text: 'The nickname "Ringo" came from his habit of wearing numerous rings.',
},
{
id: 2,
text: 'Plays the drums left-handed with a right-handed drum set.',
},
{
id: 3,
text: 'Nominated for one Daytime Emmy Award, but did not win',
},
]);
}, 2400);
});
}
3 changes: 2 additions & 1 deletion example/index.js
Expand Up @@ -2,7 +2,8 @@ import App from './App.js';
import UnMountAtNode from './UnMountAtNode';
import Brahmos, { render } from '../src';
import ConcurrentApp from './concurrentApp';
import SuspenseApp from './suspenseExamples';

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

// render(<UnMountAtNode/>, document.getElementById('unmount-node'));
123 changes: 123 additions & 0 deletions example/suspenseExamples.js
@@ -0,0 +1,123 @@
import Brahmos, { useState, useTransition, Suspense } from '../src';

import { fetchProfileData } from './fakeApi';

// const initialResource = fetchProfileData(0);

export default function App() {
const [tab, setTab] = useState('home');

function showProfile(id) {
setTab('profile');
}

let page;
if (tab === 'home' || true) {
page = <HomePage showProfile={showProfile} />;
} else if (tab === 'profile') {
page = <ProfilePage />;
}

// return page;
return <Suspense fallback={<h1>Loading the app...</h1>}>{page}</Suspense>;
}

function HomePage({ showProfile }) {
return (
<>
<h1>Home Page</h1>
<Button onClick={showProfile}>Open Profile</Button>
</>
);
}

function ProfilePage() {
const [resource, setResource] = useState();

function showProfile(id) {
setResource(fetchProfileData(id));
}

return (
<>
<Button onClick={showProfile}>Open Profile</Button>
<Suspense fallback={<h2>Loading posts...</h2>}>
{resource && (
<>
<ProfileDetails resource={resource} />

<Suspense fallback={<h2>Loading posts...</h2>}>
<ProfileTimeline resource={resource} />
<ProfileTrivia resource={resource} />
<Suspense fallback={<h2>Loading fun facts...</h2>} />
</Suspense>
</>
)}
</Suspense>
</>
);
}

function ProfileDetails({ resource }) {
const user = resource.user.read();
const posts = resource.posts.read();
return <h1>{user.name}</h1>;
}

function ProfileTimeline({ resource }) {
const posts = resource.posts.read();
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}

function ProfileTrivia({ resource }) {
const trivia = resource.trivia.read();
return (
<>
<h2>Fun Facts</h2>
<ul>
{trivia.map((fact) => (
<li key={fact.id}>{fact.text}</li>
))}
</ul>
</>
);
}

function Button({ children, onClick }) {
const [startTransition, isPending] = useTransition({
timeoutMs: 10000,
});

function handleClick() {
startTransition(() => {
onClick();
});
}

const spinner = (
<span
style={{
marginLeft: 4,
fontSize: 'small',
visibility: isPending ? 'visible' : 'hidden',
}}
>
Loading...
</span>
);

return (
<>
<button onClick={handleClick} disabled={isPending}>
{children}
</button>
{isPending ? spinner : null}
</>
);
}
37 changes: 22 additions & 15 deletions src/Component.js
@@ -1,29 +1,29 @@
import { reRender } from './render';
import reRender from './reRender';
import {
UPDATE_SOURCE_FORCE_UPDATE,
withUpdateSource,
getUpdateType,
getPendingUpdatesKey,
currentTransition,
getCurrentTransition,
} from './updateMetaUtils';
import { brahmosDataKey } from './configs';

export class Component {
constructor(props) {
this.props = props;

this.state = undefined;
this.__pendingSyncUpdates = [];
this.__pendingDeferredUpdates = [];
this[brahmosDataKey] = {
pendingSyncUpdates: [],
pendingDeferredUpdates: [],
fiber: null,
nodes: null,
mounted: false,
committedValues: {},
isForceUpdate: false,
};

this.context = undefined;

this.__fiber = null;
this.__componentNode = null;
this.__nodes = null;
this.__lastNode = null;

this.__mounted = false;
this.__brahmosNode = null;
}

setState(newState, callback, type) {
Expand All @@ -39,32 +39,39 @@ export class Component {

const stateMeta = {
state: newState,
transitionId: currentTransition.transitionId,
transitionId: getCurrentTransition().transitionId,
callback,
};

const pendingUpdateKey = getPendingUpdatesKey(updateType);

this[pendingUpdateKey].push(stateMeta);
this[brahmosDataKey][pendingUpdateKey].push(stateMeta);

reRender(this);
}

forceUpdate(callback) {
withUpdateSource(UPDATE_SOURCE_FORCE_UPDATE, () => {
this[brahmosDataKey].isForceUpdate = true;
reRender();
if (callback) callback(this.state);
});
}

__handleError() {}

__render() {
// get the new rendered node
const nodes = this.render();

// store the current reference of nodes so we can use this this on next render cycle
this.__nodes = nodes;
this[brahmosDataKey].nodes = nodes;
return nodes;
}
}

export function isClassComponent(element) {
return element.prototype instanceof Component;
}

export class PureComponent extends Component {}

0 comments on commit 42267ed

Please sign in to comment.