dyo / dyo Goto Github PK
View Code? Open in Web Editor NEWDyo is a JavaScript library for building user interfaces.
Home Page: https://dyo.js.org
Dyo is a JavaScript library for building user interfaces.
Home Page: https://dyo.js.org
x = h('span');
setState = null;
Root = function() {
setState = this.setState.bind(this);
return h('div', this.state.showText ? 'test' : x);
};
Root.getInitialState = function() {return {showText: false};};
render(Root, $el = document.createElement('div'));
console.log($el);
setState({showText: true});
console.log($el);
setState({showText: false});
console.log($el);
<!-- Expected -->
<div><div><span></span></div></div>
<div><div>test</div></div>
<div><div><span></span></div></div>
<!-- Recieved -->
<div><div><span></span></div></div>
<div><div>test</div></div>
<div><div>test</div></div>
Hi, I'm sorry but I can't find any online sandbox supporting aliases to provide you a link so that you could easily test it.
Basically, with webpack, one can add aliases, which can be quite useful when trying React libraries with React-compatible alternative libraries such as Dio.js, Inferno.js, Preact and NervJS. If you create aliases for both 'react$' and 'react-dom$' pointing to 'dio.js', then you should be able to run this example from the MUI-React documentation:
https://www.muicss.com/docs/v1/react/introduction
Just include the CSS as explained in the introduction and run their full example. It mostly works, but the button text is duplicated for some reason. It works fine with both React (of course) and inferno-compat aliases, so it's probably some tricky bug in Dio.js. I'd appreciate if you could figure out what's causing this behavior and fix it. I'd love to use Dio.js in my project, as it's more lightweight than Inferno.js and I also enjoy having more than one option regarding React alternatives. I tried Material UI before MUI, but not only Material UI is big but it also makes explicit use of the synthetic events API which are not supported by Dio.js (Inferno does support them, so it works with inferno-compat, but if I were able to use MUI with Dio.js, that would be very helpful).
I hope you have some time and interest in making it easy for Dio.js users to use existing React UI libraries through aliases in their webpack projects :)
Thanks for Dio.js :)
class Component {
componentDidThrow(err) { return err }
render() {
throw new Error('x')
}
}
console.log(`${h(Component)}`)
// expected
''
// got
`<pre>{
"location": "render",
"message": {}
}</pre>`
{h, hydrate} = require 'dio.js'
Root = ->
h('div', 'abcabcabc', 'xxx')
$el = document.createElement 'div'
$el.innerHTML = '<div>buy now</div>'
hydrate Root, $el
Error: Failed to execute 'splitText' on 'Text': The offset 9 is larger than the Text node's length.
at getDOMQuery (build/tests.js:21897:14)
at commitQuery (build/tests.js:21029:4)
at commitMount (build/tests.js:20789:11)
at commitChildren (build/tests.js:20742:4)
at commitMount (build/tests.js:20804:3)
at commitMount (build/tests.js:20764:5)
at mount (build/tests.js:21616:3)
at mount (build/tests.js:21604:11)
at hydrate (build/tests.js:21580:3)
at Context.<anonymous> (build/tests.js:26814:12)
As this is the first issue I've opened for "dio.js" let my first thank @thysultan et al. for that really great DIO library.
Like the title already says, the method "setState" does not update "this.state" if the method "shouldComponentUpdate" returns false (DIO 8.x and 9.x).
This is working properly in React.
This is a breaking change (semver), and seems incorrect
123dd80#r26955261
This seems wrong? Mounting is the act of DOM insertion, and should definitely never be called server-side
I can't seem to find where a node's attrs
property is either set or used
Also the test for it is bugged
https://github.com/thysultan/dio.js/blob/70dbe849b701d484373fd10d1733157fe661f53d/tests/Element.spec.js#L16 props instanceof Object
hey @thysultan
was wondering if you were planning on submitting a keyed v6 or v7 implementation of js-framework-benchmark. curious how it would perform :)
Problem:
Third party libraries tend to like adding resources dynamically to <head>
at run-time. Additionally, currently the initial diff re-writes resource tags causing re-requesting (e.g. <script src="xxx">
)
Original Solution:
I wrote a custom diff for <head>
which only diffed <link>
, <meta>
and <title>
tags
Proposed Solution:
blank fragment placeholder, allow appending to DOM node
e.g.
<head>
<title>xxx</title>
h(BLANK_FRAGMENT)
then a library could append to <head>
without altering diffing
I have no clue whether this makes sense. Another Idea I had was to mark original DOM nodes as editable
(all new dio nodes would also be marked), then diff would ignore non-editable
DOM nodes. (this doesn't fix re-requesting on initial diff)
for function components, only one of didMount or willMount is ever called, depending on which was defined last
function render() {
return h('div')
}
render.componentDidMount = function() { console.log('did mount') }
render.componentWillMount = function() { console.log('will mount') }
// 'will mount'
render.componentWillMount = function() { console.log('will mount') }
render.componentDidMount = function() { console.log('did mount') }
// 'did mount'
expected
// 'will mount'
// 'did mount'
possibly related, this is dead code
https://github.com/thysultan/dio.js/blob/70dbe849b701d484373fd10d1733157fe661f53d/src/Boundary.js#L78
if a component has componentDidThrow
defined, it should not log to the console
v7.0.0 is a new version that will come out soon, this issue acts as a issue tracking on the progress including changes and new features that will come with it.
dio.fetch
top level APIprogress
to http API dio.fetch('', {progress: (e) => {}})
)dio.module
top level API.done
rendering state.dio.request(options)
, in favour of fetch(url, options)
V7.0 will introduce a few breaking changes if something other than the high level api's createElement
or h
are being used to create virtual element this includes createFactory
but excluding DOM
, other than that nothing else should break.
First class support for lazy loading components will allow for the following asynchronous component loading pattens.
// Components/Bar/index.css
& {
color: red;
}
// Components/Bar/index.js
class Bar extends Component {
render ({message}) {
return h('h1', message);
}
}
export default Bar;
// entry.js
const {fetch, Component} = dio;
class Slot extends Component {
stylesheet () {
return fetch('Components/Bar/index.css');
}
render () {
return fetch('Components/Bar/index.js', {
body: {message: 'Hello World'},
progress: h('h1', 'Loading Component...')
})
}
}
dio.render(Slot);
When render is called Slot progress element <h1>Loading Component</h1>
will mount. After the index.js
file is loaded into the browsers VM and the exported component in index.js
replaces the progress element on the other hand index.css
is loaded, name-spaced and added to the page. If a function is passed as opposed to a virtual element as the value of a property progress
on the optional second argument of fetch()
will expose the ProgressEvent
object as a single argument.
dio.fetch
will mimic for the most part the window.fetch, the introduction of this API also introduces context aware request that makes returning a http request object from an render method.
fetch('foo.js', {
progress: ({loaded, total, timeStamp}) => {
console.log(total - loaded, timeStamp);
}
})
dio.module
is the low-level module loader that fetch
uses when returned from a render method.
// entry.js
module('foo.js').then((foo) => {
foo.add(1, 2);
})
// foo.js
const add = (a, b) => a + b;
export {add}
If you wanted to transpile modules on the fly before consumption dio.module
also allows you to register one via dio.module.transpile = fn
This may be a feature or bug depending on how it's view but currently when a coroutines component reaches a done
state it is unmounted on the next render. v7.0.0 will change this behavior to prevent unmounting when done
and the subsequent .next()
returns nothing.
This was caused by upgrading from 8.0.0 -> 8.1.0
{h, render} = require 'dio.js'
A = -> h 'div'
A.componentDidMount = -> console.log 'mount A'
A.componentWillUnmount = -> console.log 'unmount A'
B = -> h 'div'
B.componentDidMount = -> console.log 'mount B'
B.componentWillUnmount = -> console.log 'unmount B'
setState = null
cachedState = null
Parent = ->
setState = this.setState.bind(this)
h('div', this.state.child)
Parent.getInitialState = -> cachedState or {child: h('div', A)}
setStateRoot = null
Root = ->
setStateRoot = this.setState.bind(this)
h('div',
if this.state.isMounted then Parent else null)
Root.getInitialState = -> {isMounted: true}
render(Root, document.createElement 'div')
setState(cachedState = {child: h('div', B)})
setStateRoot({isMounted: false})
setStateRoot({isMounted: true})
setState({child: h('div', A)})
setStateRoot({isMounted: false})
# Expected
mount A
mount B
unmount A
unmount B
mount B
mount A
unmount B
unmount A
# Received
mount A
mount B
unmount A
unmount B
mount B
mount A
unmount B
unmount B
<div onclick="function() { ... }"></div>
should be
<div></div>
Just a short question:
DIO does not have support for property validation (aka. "PropTypes"), does it?
(it seems that DIO version 3.4.0 did have some support for "propTypes")
How can I embed a 3rd party DOM node within a vtree?
e.g. I want to embed a CodeMirror node
Editor = _=> h('div', h('textarea'))
Editor.componentDidMount = ($el) => CodeMirror($el.querySelector('div > textarea'))
Editor.componentWillUnmount = ($el) => CodeMirror.destroy($el.querySelector('div > textarea'))
The CodeMirror
constructor hides the textarea
and appends it's own dom tree to the parent node
<div>
<div class="code-mirror"><div></div></div>
<textarea style="display:none"></textarea>
</div>
Some unit tests will prove every thing is work as expected and help us to contribute will PRs โญ
The server side rendering example includes the entire page DOM
https://dio.js.org/api.html#server
However it is not possible to render to the document object.
What is the expected way to diff the <head>
block?
the major use case is the <title>
tag, but also having a dynamic manifest.json for marketplaces (e.g. clay.io (mobile))
Webpack hot module replacement is a fantastic tool for web development (which I utilize extensively).
I've been attempting to get dio to support it but have been unsuccessful so far.
If you have the time I'd be grateful if you gave it a shot.
I've created the following repository for experimentation:
https://github.com/Zolmeister/dio-hmr
How to add code coverage with istanbul:
npm i -D nyc
# npm i -D graceful-readlink
// package.json > "scripts"
"test": "nyc --reporter=lcov --reporter=text mocha --require script/test/index.js"
now when test
is run, code coverage will print to console, with a detailed report available at
/coverage/lcov-report/index.html
e.g. getDefaultProps()
is never hit by tests
A curried function is a function that can be partialy applied and executes once the number of provided arguments is equal to the function arity, example:
var curried = curry(function(a, b, c){
console.log(a + b + c)
})
curried(1)(2)(3)
curried(1, 2)(3)
curried(1)(2, 3)
// the three calls logs 6
AFAIK dio.curry
does not returns a curried function, it returns a function wrapped with fixed predefined arguments. To avoid confusion the renaming should be taken in consideration. My proposal is:
I prefer dio.defer
dio.partial
DIO's 9er version does NOT preselect the first item of select boxes by default.
This was working fine in DIO's 8.x versions.
{h} = require 'dio.js'
class Root
componentDidCatch: ->
console.log 'should be called'
render: ->
throw new Error 'test'
h(Root) + ''
should be called
is not logged (works in 8.2.4)
Given the following web component.
class AppDrawer extends HTMLElement {}
You could theoretically render it in the either one of the following ways.
// define localName
window.customElements.define('app-drawer', AppDrawer);
// render using localName
render(<app-drawer></app-drawer>)
// render using constructor
render(<AppDrawer />)
The second method does not work, and though the first method does, the second feels more natural coming from components as functions/classes. This issue asks the question of "I wonder if we should add support for the second".
If the DOM renderer auto generates a localName for web components we can support both methods and insure that both the problem of duplicate defined localNames and undefined localNames does not occur, though this might mean some component names would have a prefix/suffix in their name in dev-tools.
Since new WebComponentClass()
generates a DOM node we could use this, but it's uncertain whether the different web-component polyfills support this pattern of creating a web-component and we might still run into the problem of name conflicts or undefined localNames, so preferably this would be wrapped in a try-catch to catch those scenarios.
Either method if implemented should be in such a way that custom renderers can employ their own heuristics for custom elements.
Hey, guys!
Nice work in this library!! But, I had played with it a little and I found a bug.
7.1.0
The state
and setState
aren't in sync.
setState
is called, it should update the state property and trigger the rendering again, if there are changes.state
using the setState
, it will always contain an empty object as the state
.inspired by fiber, dio v5.2.0 might introduce error boundaries, this issue will document possible implementation details.
class Foo extends dio.Component {
componentDidThrow (err) {
switch (err.location) {
case 'componentDidMount': return h('h1', 'something bad happened in did mount');
case 'render': return h('h1', 'something bad happened in render');
}
}
componentDidMount () {
throw 'pawned!';
}
render () {
return h('h1', 'Hello World');
}
}
After componentDidMount throws, it will first render nothing from the Foo component and continue to the end of the stack at which point it will then re-try to render the Foo component, if it still fails it will then capture the error and pass it to componentDidThrow
if it exists, if componentDidThrow
returns something that can render(element/string/array) dio will render that else continue to render nothing. If componentDidThrow
is not defined it fallbacks to the default error handler that prints a console.error
of the error and continues to render nothing.
Dio caught an error thrown by "Foo", the error was thrown in "componentDidMount".
The object passed to componentDidThrow
will have the shape
{
from: 'Foo',
location: 'componentDidMount',
message: 'pawned!',
stack: 'js stack trace...'
}
render
, lifecycle methods and callbacks passed to setState
and forceUpdate
will fall within the areas that are supported by error boundaries.
About .request
, maybe I'm missing something, then
and catch
are working correctly but I never see err
populated, always empty, and while I get the response object from then
I can't check the server status which may return an application error (eg: 422 data validation failed).
Currently I'm using a workaround saving xhr
from config: function(xhr)
but I guess this is not how is supposed to work, what I'm missing?
It will look something like
const Heading = h => ({
async getInitialProps (props) {
return {name: 'World'};
},
render ({name}) {
return h('h1', 'Hello ' + name);
}
});
dio.render(Heading, '#root');
when getInitialProps
returns a promise/stream the component will fall through to async rendering.
I wonder if adding jsx
syntax support (probably a babel/webpack/rollup plugin) would be interesting for those users who already like react-like libraries.
So I can write the Welcome component as
class Welcome {
render() {
return <h1>Hello ${this.props.name}</h1>;
}
}
Any plans or work in progress?
https://github.com/thysultan/dio.js/blob/master/types/typescript.d.ts#L363
ERROR in $PATH/node_modules/dio.js/types/typescript.d.ts
(363,52): Cannot find name 'P'.
ERROR in $PATH//node_modules/dio.js/types/typescript.d.ts
(363,55): Cannot find name 'S'.
tsc -v
Version 2.6.2
tsconfig.json
{
"compilerOptions": {
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "esnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"strict": true, /* Enable all strict type-checking options. */ /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node",
"jsx": "React",
"jsxFactory": "dio.createElement"
}
}
I wanted to open this issue so folks using DIO know about it. There are a few similar bugs in other libraries.
DIO can listen to native DOM events dispatched from Custom Elements. However, it uses a heuristic to convert JSX event binding syntax into event names, and always lowercases the events. For example onFooUpdated={handleFoo} tells DIO to listen for an event called 'fooupdated'. This means DIO can support events with lowercase and kebab-case names, but not camelCase, PascalCase, or CAPScase events (e.g. 'URLchanged').
From https://custom-elements-everywhere.com/#dio-handling-events
Preact tracking bug - preactjs/preact#788
Polymer tracking bug - Polymer/polymer#4888
Stencil tracking bug - ionic-team/stencil#373
Feel free to close if you don't think this is an actual issue or you don't intend to work on it. The main objective of my tests is just to outline what will and won't work so developers can figure out best practices.
@thysultan ,
requiring dio 8 with brunch generates the following error:
Error: Cannot find module 'dio.js/dist/node' from 'dio.js/dist/umd.js'
Something went wrong trying to import the server module.
I don't use SSR so I can ignore the error, but it is annoying for my users.
Dio v7.x does not have this issue.
Anyway, this is a great library. Thanks for your work!
Hi, I find it interesting that I can use the same code with React, Preact and Inferno by simply importing the equivalent h
, render
and Component
from them. I'd love to be able to do the same with Dio.js, but it seems Dio's API is not compatible. For example, inside a component's render
I'd have access to the props with this.this.props
instead of this.props
. Wouldn't you be interested in keeping the same compatibility layer in the basic API, just like Preact and Inferno do? This way, authors of React-like application can easily switch their VDOM implementation and compare them.
v8.2.3
setRootState = null;
setWrapperState = null;
Child = function() {
return h('div', 'child');
};
Wrapper = function({children}) {
setWrapperState = this.setState.bind(this);
return h('div', children);
};
Root = function() {
setRootState = this.setState.bind(this);
return h('div', h(Wrapper, h(Child, ['xxx']))); // NOTE: non-empty child-array
};
render(Root, document.createElement('div'));
setRootState({x: 'root'});
setWrapperState({x: 'wrapper'});
TypeError: Cannot read property 'id' of undefined
React supports the defaultValue option for several form elements, which is often used with uncontrolled components: https://reactjs.org/docs/uncontrolled-components.html
Is Dio.js going to support those as well?
https://github.com/thysultan/dio.js/blob/786f2dbac6fee35db60741d405da841ad9f20237/docs/examples.html#L22
https://github.com/thysultan/dio.js/blob/b1783101f9d2e5b43dfbe589719665e84f620de3/docs/api.html#L22
https://github.com/thysultan/dio.js/blob/205b08a7eb0b4b52ab68367cba5173944d17716b/docs/index.html#L22
https://github.com/thysultan/dio.js/blob/b1783101f9d2e5b43dfbe589719665e84f620de3/docs/examples/particles.html#L26
https://github.com/thysultan/dio.js/blob/b1783101f9d2e5b43dfbe589719665e84f620de3/docs/examples/5k.html#L25
https://github.com/thysultan/dio.js/blob/eef5a5d04054e76dbef6ed938ee8eeae12f63461/docs/introduction.html#L22
https://github.com/thysultan/dio.js/blob/eef5a5d04054e76dbef6ed938ee8eeae12f63461/docs/introduction.html#L372
Hi, take a look at this example app: https://codesandbox.io/s/0x1xj6m60p
You'll notice that while both React, Inferno and NervJS set the reference to component in the right order while clicking on the "Second" button (null is logged before the new reference), both Dio.js and Preact seem to process them in the wrong order (new reference, then null).
When implementing a client-side routing while willing to keep a reference to the current app, it's very important that this order should work like React, Inferno and NervJS. Would you mind fixing this, please?
It seems like React will deprecate componentWillMount and componentWillUpdate. Are there any plans to support the new hooks getSnapshotBeforeUpdate / getDerivedStateFromProps?
Given DIO's ability to render Promises, the mentioned components may server as good abstractions for these.
React currently uses a combination of componentDidCatch
and throw
for the control flow of this, we may be able to do the same or directly use Promises since DIO is able to render them.
Hopefully we can try to implement a POC and see along the way if anything needs to change with the internal implementation of componentDidCatch
to make this work.
If I understood the errors handling section of the React documentation correctly it does not catch errors from event listeners. However, it seems Dio.js does catch those errors in order to display a proper stacktrace in the console logs, which is awesome, but it ends up swallowing the error, making it impossible to install a catch-all error to report them to some service such as Bugsnag, for example.
Dio.js should either allow us to provide an option to rethrow the exception after logging it, or to install a generic error handler or to add some hook in the component such as componentEventHandlerDidThrow or something like that.
What do you suggest as an approach to report such errors (since componentDidCatch is not called in those situations) to some errors monitoring service?
The NativeScript API surface looks like a viable rendering frontier once DIO 8 lands given the layers of abstraction that went it keeping the DOM specific interface as compact and isolated as possible.
currently events are called with owner
as this
instead of the DOM node
I'd prefer unwrapped event callbacks, but if that's a deal-breaker passing the DOM node to the event handler is a reasonable alternative
e.g.
render = function() {
h('a', {
href: '/local'
onclick: function(e) { router.go this.href }
onclick: function(e, $$el) { router.go $$el.href } // alternate
})
}
https://github.com/thysultan/dio.js/blob/master/src/Render.js#L13
I believe this should be an empty string instead ''
edit:
https://github.com/thysultan/dio.js/blob/fe06a9918162b355b5aae82b0fd2bb3492e9fed5/src/Element.js#L173
https://github.com/thysultan/dio.js/blob/fe06a9918162b355b5aae82b0fd2bb3492e9fed5/src/Element.js#L296
Karma
sample karma config:
{
singleRun: true,
frameworks: ['mocha'],
files: ['./script/test/index.js'],
browsers: ['ChromeHeadless']
}
h('a', {href: undefined})
// got: <a href="undefined">
// expected: <a>
this is caused becuase of how setAttribute
works
$a.setAttribute('href', undefined)
// <a href="undefined">
also null
should probably be ignored as well
{h} = require 'dio.js'
Component = ->
throw new Error 'xxx'
Component.displayName = 'XXX'
'' + h(Component)
Recieved
The following error occurred in `
<[object Object]>
` from "render"
Expected
The following error occurred in `
<XXX>
` from "render"
There are rare cases where "forceUpdate" does not really update the view.
In those particular cases after calling "forceUpdate" the method "shouldComponentUpdate" is invoked before the intended view update, and if "shouldComponentUpdate" returns false then the view update will be cancelled.
The problem: After "forceUpdate" is called there should NOT be a "shouldComponentUpdate" check before rerendering, "forceUpdate" should ALWAYS update the view.
Those cases are quite exotic and a bit diffucult to explain - please check the component "DioCounterInfo" and "ReactCounterInfo" in the following demo to see what I mean:
https://jsfiddle.net/1q6whvye/
Please be aware that even if that use case seems really exotic and looks like kinda bad implementation, there are nevertheless real-world use cases where that bug matters - otherwise I would not have found it ;-)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.