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

Mithril Framework Adding and Breaking Unnecessarily #2558

Closed
ycadaner-merkos302 opened this issue Dec 23, 2019 · 1 comment
Closed

Mithril Framework Adding and Breaking Unnecessarily #2558

ycadaner-merkos302 opened this issue Dec 23, 2019 · 1 comment
Assignees
Labels
Type: Meta/Feedback For high-level discussion around the project and/or community itself
Projects

Comments

@ycadaner-merkos302
Copy link

BS"D

I am not the only one using mithril. I am not every day on github, but I'm seeing a repeating pattern of fixing what isn't broken.

I am seeing changes to the framework to the router, that before was solid, now the community is saying don't rely on it but use a third party.

A code base doesn't need to change every week or month, and I am seeing lots of changes to things that are not broken or just because we can - or because it's the way React does it... (If we wanted react we wouldn't be here)

It's probably good to create some policy for admitting changes. In general the foundation of the framework is based on keep it simple and bare, and if you need extraordinary additions - use a plugin. A vanilla, fast, and simple framework.

Also, there are times that bugs come up, and unfortunately mithril versions are no longer compatible because features are added on right and on left for single requests that nobody else needs and a scenario that can be tailored to that specific case, and not the framework.

I think it's healthy that most answers are how to make it work in mithril. But the trend has changed to let's change mithril to work for your question.

Mithril is an awesome framework, but if we turn it into Reactthril or vuethril, then it's not mithril anymore.

My immense gratitude to all contributors toward making Mithril better!

@ycadaner-merkos302 ycadaner-merkos302 added the Type: Bug For bugs and any other unexpected breakage label Dec 23, 2019
@project-bot project-bot bot added this to Needs triage in Triage/bugs Dec 23, 2019
@dead-claudia dead-claudia added Type: Meta/Feedback For high-level discussion around the project and/or community itself and removed Type: Bug For bugs and any other unexpected breakage labels Dec 26, 2019
@dead-claudia
Copy link
Member

@ycadaner-merkos302 Many of those changes making it React-like were done by Leo Horie himself, the original creator of Mithril. And you can rest assured you're not alone in feeling Mithril is getting too React-like. 🙂

A code base doesn't need to change every week or month, and I am seeing lots of changes to things that are not broken or just because we can - or because it's the way React does it... (If we wanted react we wouldn't be here)

Some of the changes were specifically because the previous design itself was broken. m.route.linkm.route.Link was one concrete example, where I switched to a more React-like API simply because too many people were running into paper cuts having to do oncreate: m.route.link, onupdate: m.route.link if that link ever changed. I did that unilaterally because 1. it was a long-standing feature request, 2. it came up semi-regularly with users, and 3. it was a common source of gotchas. We've also made changes to other features in the past for similar reasons, like fixing m.redraw() to always be async and creating a separate m.redraw.sync() for sync redraws.

Also, Mithril doesn't release updates that quickly - we move fairly slow. Compare that to React, which in the last four five updates:

  • v16.5 on 2018 Sep: Add support for React DevTools Profiler, add a production build variant with support for profiling.
  • v16.6 on 2018 Oct: Add React.lazy, React.memo, static contextType. All three decent-sized features.
  • v16.7 on 2018 Dec: A slightly breaking perf bug fix for React.lazy.
  • v16.8 on 2019 Feb: Add the Hooks API. (In Mithril, this would've been a major bump, not a minor bump.)
  • v16.9 on 2019 Aug: Async act() support, a lot of new deprecations.
  • The next minor release will likely have Concurrent Mode and Suspense for Data Fetching stabilized.

Here's the last 5 releases for Mithril:

  • v1.1.6 on 2017 Dec: One minor bug fix, two major bug fixes.
  • v2.0.1 on 2019 Jul: The first v2.0 release.
    • Note: v2.0.0-rc.0 was released on 2018 Oct.
  • v2.0.3 on 2019 Jul: Major security fix
  • v2.0.4 on 2019 Aug: Two small bug fixes
  • v1.1.7 on 2019 Sep: Port of security fix in v2.0.3 to v1.x + minor bug fix.
  • The next minor release is slated to carry a handful of usability improvements and to lose a slew of undocumented internal-only development utilities that should've never been shipped in the first place.

Couple critically broken, unpublished releases excluded. I also excluded all the other release candidates for brevity.

We just had a major release, so that skews things a lot, but we do still move much slower than React does. We also try to be much smaller in code size, as that does have a major impact on startup, and this necessarily means being more judicious on what we do add.

I think it's healthy that most answers are how to make it work in mithril. But the trend has changed to let's change mithril to work for your question.

Mithril has always been very oriented towards getting out of the developer's way. My thing is that I've been trying to take that even further, by altering Mithril to continue getting out of people's ways. And in some cases, it means making it more intuitive so you aren't having to think as hard when using it. Ideally, you shouldn't have to fight the framework at all.

You're probably just looking at the feature requests and some of the early v3 investigation.

  • Error handling and recovery #2273 is about error handling (or the lack thereof), and fixing that is going to be difficult to do without breaking things. I did come up with a minimally breaking design in my latest comment there, though, so it has a shot in making it in a future v2 minor release.
  • add React Context API similar feature #2148 is about not having a context API like React's. I have zero appetite for React's specific design, and would rather just use plain (frozen) objects - it's easier to implement that way as well. However, this does make it easier to make routing much more dynamic, and letting routers be composable and nested inside page layouts simplifies their use quite a bit in both medium-size and larger applications that use client-side routing.
    • Add a built-in component for async view loading #2282 is related and synergizes with that kind of router change, and together with m.route.SKIP eliminates the need for onmatch. It also has more general uses, of course. When you combine these two, that's what opens up the possibility of Simplify the router to just use render functions #2506, which is pretty unlike React Router in its hybrid static/dynamic design. And trust me when I say this: our current router design is broken. I have to field far too many questions on how to do specific things, and even basic things like page layouts are unnecessarily complicated.
    • This would also make it much easier to integrate with state libraries and test components using them, as you wouldn't have to make the trade-off of usability (globals) vs testability (pass as attributes), but have the best of both. I'm not just talking of Redux integration, but also of even simple traditional models. And yes, this is an area where people do genuinely struggle.
  • Extend (present, previous) signature to all component methods  #2098 is very explicitly anti-React, but is broadly popular among those of us long-time contributors. We've also been considering bringing back something like v0.2's {subtree: "retain"} (likely in the form of m.RETAIN) for v3, and if we combine that with this, onbeforeupdate immediately becomes redundant with view, and oncreate + onupdate can be merged into a single lifecycle hook if you pass null for the previous attributes on first call.
  • Feature request: Custom renderer support #2215 is mostly just me trying to figure out what shared concerns actually exist between different kinds of renderers, so I know what precisely needs to be done to help those happen, not an actual proposal for something like React's custom renderer backend API. I'd like to see Mithril in more than just HTML generation and DOM-based applications - it'd be super cool to see it in, say, VR, native mobile, or interactive command-line apps. I've also talked to people who specifically passed up Mithril because it lacked anything that would've helped make custom renderers easier to write.

In much of this, I've been noticing survivorship bias at work, with occasional stories of why people stop using Mithril and why people chose to skip Mithril (usually in favor of React), despite being impressed by the framework. Their reasons are as valuable as the regular complaints I normally get with various aspects of Mithril, and are often far more informative of where the issues truly lie. Some things do truly need fixed, but the issues at play are much more in-depth and multi-faceted, and so I have to address them in multiple spots at once. Sometimes, it's as simple as adding a small feature, like m.route.SKIP. Sometimes, it's a small design tweak, like splitting v1's sometimes-sync m.redraw() into the more consistent m.redraw() and m.redraw.sync(). Sometimes, it requires completely overhauling the API, like m("a", {...attrs, oncreate: m.route.link(opts), onupdate: m.route.link(opts)}, ...)m(m.route.Link, {...opts, ...attrs}, ...).

Also, about half of these issues I linked to here have a fairly long history of discussion in our Gitter channel, here in our issue tracker, and sometimes elsewhere. Very little of this is even new.

I am seeing changes to the framework to the router, that before was solid, now the community is saying don't rely on it but use a third party.

That's not something I've been hearing. Most of those recommendations are people wanting stuff out of a router that not even the most massive of frameworks like Rails, Ember, and Angular offer out of the box, and I'm adding literally the bare minimum (like m.route.SKIP, which is also separately useful for other reasons) to un-block them. But this is for a very small minority coming with very advanced issues.

Mithril is an awesome framework, but if we turn it into Reactthril or vuethril, then it's not mithril anymore.

Trust me: none of us active or former developers want to turn Mithril into anything React-like. Leo was heavily inspired by React in his v1 rewrite a few years ago, but we've been slowly undoing that ourselves, with even some of the initial rewrite becoming less React-like before it first went stable. I've personally been working on a redesign that captures the simplicity and power of Mithril without nearly the drawbacks of the design hacks that v0.2 suffered with extensively (like m.startComputation()/m.endComputation()) or the unnecessary OO-isms that plague v1 and v2 (like onbeforeupdate and m.request's type parameter). So don't worry. 😉

I do want to point out that some things one might think are React-isms aren't even React-specific and are just part of the immediate mode tree-patching UI paradigm that virtual DOM libraries and frameworks inhabit. And some are more general than even that. For example:

  • Virtual trees, whether modeled directly with objects (like Mithril and React) or indirectly with procedural calls (like Incremental DOM and Dear ImGui), are very much necessarily part of the paradigm.
  • A set of target bindings to render to, whether it be the DOM (like Mithril and React DOM), native UI objects (like React Native), or even OpenGL or Vulkan (like Dear ImGui), is innate to every UI library and framework out there that manages anything on its own.
  • A way to link a subtree to state, whether handled via userland idioms (like Elm, apps using Meiosis, and Dear ImGui), entirely managed by the renderer itself (like most virtual DOM frameworks), or injected into the framework (like Vue, Angular, and Backbone), are innate to all UI frameworks and libraries. Sometimes, these get split into "views" and "controllers" or "views" and "view models", but they still necessarily act in pairs in this case.
  • A way to initiate a redraw (like Mithril's m.redraw() and React's this.setState(newState)) is innate to all UI frameworks.
  • A way to provide the library or framework a tree to render is innate to all UI frameworks, but having a way to pass dynamic trees to the library or framework is unique to the tree-patching UI paradigm - without this, you don't have a UI.
  • If primitives map directly to an underlying tree structure, a way to access those for third-party integration and more advanced configuration is essential for virtually every UI library and framework I'm aware of that manages the creation of nodes in that underlying tree structure.
  • Subtree lifecycle, whether bound to components or not, is essential to all UI frameworks and libraries, and without it, it becomes much harder to use. In particular, Incremental DOM suffers a lot from a lack of lifecycle support, and so it has continued to remain fairly niche. Elm struggles in the area of third-party integration for similar reasons.
    • Every UI library and framework that manages components and/or render times itself features a hook that runs on render, and this hook is commonly fused with the ability to provide trees to the library or framework, especially when the tree is dynamic, but it can exist without it (like with Svelte).
    • Most UI libraries and frameworks feature the ability for you to know if this is the first render or a subsequent render. This is commonly left implicit in the model, though.
    • Most UI libraries and frameworks feature a hook that runs after the tree is fully committed. This is commonly split into two hooks based on the last bullet, like oncreate/onupdate in Mithril and componentDidMount/componentDidUpdate in traditional React, but not always, like useEffect in React Hooks.
  • A way to react to user input and other related external input is essential to every UI library and framework. A few, like Dear ImGui, use return values, but most, including Incremental DOM, use callbacks for this purpose.
  • The ability to selectively prevent updates to a subtree, whether linked to subtree lifecycles or not, is an essential optimization for all virtual DOM frameworks. Some leave this implicit (React Hooks via React.memo, Elm in all cases), some make this explicit (shouldComponentUpdate in React, onbeforeupdate in Mithril v1/v2, {subtree: "retain"} vnode in Mithril v0.2).

This is by no means complete (keys for one), but should hopefully give you a better idea of where things differ.

My immense gratitude to all contributors toward making Mithril better!

Many "thank you"s! Much appreciated! 🙂

Triage/bugs automation moved this from Needs triage to Closed Apr 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Meta/Feedback For high-level discussion around the project and/or community itself
Projects
Development

No branches or pull requests

3 participants