Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactive Hooks and Updates - Major Update - v9 #135

Merged
merged 32 commits into from
Dec 24, 2019
Merged

Reactive Hooks and Updates - Major Update - v9 #135

merged 32 commits into from
Dec 24, 2019

Conversation

JRJurman
Copy link
Member

@JRJurman JRJurman commented Dec 15, 2019

Summary

This PR is a major rewrite / refactoring of how tram-one works, making individual components capable of updating themselves when their state updates. This is a huge performance boost over the existing implementation that mimics React's methodology to re-render the entire app when state changes.

To better understand the motivation behind these changes, watch Rich Harris's talk on Rethinking Reactivity https://youtu.be/AdNJ3fydeao

Beta Release

Currently, the contents of this branch are released on npm under the beta tag, so you can test these changes by installing tram-one@beta. The version should be something like v9.0.0-beta.0

npm install --save tram-one@beta

You can also install directly from this branch:

npm install --save Tram-One/tram-one#reactive

Codesandbox

If you want to play around with an example (based on the tutorial), you can see this code sandbox
https://codesandbox.io/s/tram-one-reactive-beta-o6x4j

image

You can even see in the demo above that the components only re-render when their state changes (without useMemo 😄)

Changes

Observables & Reactions

  • internal change
  • user-facing change

Instead of useState and useGlobalState, there are two new hooks, useObservable and useGlobalObservable. These hooks give access to an observable object (created using nx-js/observer-util ) that when updated or set will trigger JUST those components using that state to update.

This removes the need for useMemo, as components will only trigger when the state they are listening to changes.

Effects are also observed, and so if state that they are dependent on changes, they will independently (from the component) re-trigger as well. This removes the need for dependencies or trigger in the useEffect.

Components and effects are triggered / unobservered using the browser's MutationObserver, which tracks which new components are added, removed, or replaced.

Better Docstrings

  • user-facing change

Instead of having adjacent .doc.js files, the refactoring (mentioned below) has allowed the code to be properly annotated with jsdocs.

image

Updated API Docs

  • user-facing change

The examples have been updated to include live StackBlitz for each function. This should be more valuable and exhaustive then the existing static examples, and better than the tutorial for giving people an idea for how to use the functions.

image

Node Properties

  • internal change
  • slight user-facing change

In order to properly update components individually, we are now storing properties on the mounted DOM. An individual element will have properties like tram-tag-reaction, tram-tag-new-effects, and tram-tag-cleanup-effects. These properties allow us to process the elements in the mutation observer.

Remove / Revert Dependencies

  • internal change

Since we are updating the individual component, we are no longer dependent on nanomorph (or it's fork, tatermorph).

Since we are using observables, we no longer need to manage state using hover-engine.

For some reason we still had rlite-router in our dependencies, but that has been extracted out to use-url-params.

Refactoring Globals

  • internal change

Previously all functions were wrapped in a function to take in a global. This has been refactored so that they get whatever the currently set global is from a function. This makes the code easier to read and easier to add to.

- const registerHtml = globalSpace => registry => {
-	return registerDom(globalSpace)(null, registry)
+ const registerHtml = registry => {
+	return registerDom(null, registry)

Remove Render-lock

  • internal change

Because we are no longer doing full sweeping renders of the app, we can remove the render-lock (a mechanism for stopping rendering loops in response to effects).

Mount mimics component rendering

  • internal change
  • slight user-facing change

Mount follows similar logic to how we create and process other components. Sadly, for reasons that I mostly understand but can't quite wrap my head around, in order for this to work the app needs to be wrapped in a surrounding element (right now a div).

Remove custom asserts / internal asserts

  • internal change
  • slight user-facing change
    Some asserts wouldn't have an impact on the end user, so those have been removed. We are now using the library type to manage verifying types.

testing-library

  • project change
    instead of unit tests on the functions themselves, we are writing tests that start an app and verify the DOM has the expected results. This is made possible with Testing Library, which gives access to the jsdom, and allows us (in unit tests) to query and interact with it like a headless browser. This is valuable because with code-coverage, it's easier to find dead branches, we are testing the code in a more real environment, and we don't have to mock out internals to verify functions work as expected.

Todo

  • Investigate using non-forked dependencies (since we no longer need getEvents)
  • Validate routing updates with url-listener
  • Update unit tests
  • Update integration tests
  • Update Website Docs
  • Update Tutorial
  • Resolve Build Warnings

Post-Release Todo

  • Update tram-one-express

Copy link
Contributor

@ethanjurman ethanjurman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked through some of the smaller files, if I get time later I want to review the bigger stuff soon.

package.json Outdated
@@ -1,6 +1,6 @@
{
"name": "tram-one",
"version": "8.1.3",
"version": "9.0.0-beta.2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it okay to have the beta version in a master branch? or is the versioning managed by npm?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we do the final merge, it'll be just 9.0.0

src/hooks/observableHook.js Outdated Show resolved Hide resolved
src/tram-space/index.js Outdated Show resolved Hide resolved
configs/rollup.config.cjs.js Show resolved Hide resolved
integration-tests/mock-components/graphic.js Outdated Show resolved Hide resolved
integration-tests/mock-components/home.js Show resolved Hide resolved
src/mount.js Show resolved Hide resolved
src/mutation-observer.js Show resolved Hide resolved
@JRJurman JRJurman merged commit 84c1ee3 into master Dec 24, 2019
@JRJurman JRJurman deleted the reactive branch December 24, 2019 04:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants