Hacker News new | past | comments | ask | show | jobs | submit login
Things Reacters do that Re-framers avoid (purelyfunctional.tv)
110 points by zonotope on Sept 2, 2017 | hide | past | favorite | 51 comments



By chance, I stumbled upon this same article a few weeks ago while re-learning React and learning Redux.

I didn't get much out of the article since gripes 1-5 are all solved by Redux, and 6 is a complaint about Redux having too much boilerplate.

It also seemed to me that mapStateToProps and mapDispatchToProps aren't given a fair representation in this article (especially with the "selector" pattern emerging within them). If your Redux store is denormalized already, then yes, mapStateToProps will seem quite redundant. However, if you're working with multiple sets of async data, it's much easier to organize your store by fetch method and denormalize inside mapStateToProps.


Yep, and as I've said numerous times [0] [1]: you can use as much or as little abstraction on top of Redux as you want. Don't like writing action constants by hand? Use `redux-actions` to generate action creators that have an overriden `toString()`. Don't like touching multiple files? Use the "ducks" pattern. Want something that adds additional layers on top so you don't even have to touch the store? Use Kea or Jumpstate or Dva.

Also, my own personal advice is that you should always use the "object shorthand" version of `mapDispatch`, and never actually write a real `mapDispatch` function by hand again:

    const actions = {addTodo, toggleTodo};
    export default connect(mapState, actions)(TodoList);

[0] http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao...

[1] https://github.com/reactjs/redux/issues/2295


Having to use something like Redux, conceptually solid but so full of boilerplate, instead of re-frame is kind of like having to use Java at work instead of Clojure.

Some of us are just lucky, I guess.


So true. Some of us are lucky enough to use a concise language like clojure. I am really asking myself why lisp and functional programming in general are not really taught in schools.


I learned Scheme in my first semester of computer science (in Germany). Back then, I thought "What's this garbage? I want to program a game or a webapp, not some obscure stuff in a language I never heard of".

I can appreciate the idea of teaching functional programming only after I graduated, but for starters, it seems too far away from what the industry, magazines and people talk about. That might be a reason why it's not taught at many schools.


Functional programming is taught in schools. Sometimes even with lisp. Students unvirsally seen it as impractical theory.

The popular ressurection of functional on frontend is relatively new development. Before, it was used only in few math related areas.


Functional programming is widely used in finance.


I would count that as one member of "few math related areas".


> 1. Reacters load data on mount. I want my Views to be pure. They need to take some data and output DOM.

At least this item comes off as a straw-man argument. Reacters also want their views to be pure. They do not typically load data in presentational components, and it can be easily avoided.

That said, granted: Clojure, Elm et al. achieves purity and immutability in a simple and clean way JS/React/Redux can only approximate. The latter is for the dirty pragmatists among us, I guess.


Somewhat strawman, yeah. But it still seems valid - React allows you to do this, and you do see it in the wild.

People who have been bitten by it (or read enough best-practices stuff to avoid it up-front) will know better and won't do so, but you have to know better - that's kinda the central point in here. React has more footguns, and the article is arguing that they're unnecessary / things end up worse than if they weren't allowed, without losing flexibility.


This article fails to understand what components are in React. They are the building blocks for every aspect of an application. My data stores are components, my views are components, my styles are components.

The author obviously comes from an MVC background where a component is one specific aspect of an application, and enabling it to do all these other things seems sacrosanct. But they've got it backwards.

Think of a React component on the same level as a function: we design our functions as purely as we can, but it's not the function's fault if it's written poorly.


The author also mentions why they see this property of React as a problem.

This is just like earlier versions of Java which didnt have enums or lambdas, but used classes for everything. See what's happened now.


EDIT: I also think you've severely misinterpreted the article; re-frame (the library/framework that builds on top of reagent, which is a wrapper for React in clojurescript) is basically Redux's suave older brother IMO.

[EDIT: Regarding "everything is a component"] And this is a weakness. If everything is a component, everything is tied to the React library/ecosystem.

It would be better to treat everything as a primitive - like, say, a function. My data stores are functions (closures), my styles are functions that return data structures, my views are functions that return components.

IMO the craze that struck a lot of the React ecosystem (everything's a component! State is a component! Routes are components! Side effects are components!) is dying down and people are realizing that it's better to stick to things that are much simpler and has first-class support from the language. Tying yourself to React (or any one technology) fundamentally limits your capabilities and expressiveness, and also makes your software very brittle.

This is why, IMHO, most JavaScript devs actually would be much happier writing in Clojure(Script). The language provides first-class support for so many things that JS devs have to reach to libraries and frameworks for, e.g. immutability, CSP, pattern matching, and actual functional constructs like transducers.


I think it's more of an argument against such an understanding of a component, similar to the view that too much was packed into jQuery add-ons.


Mobx / rxjs solves a lot of these problems for those of us who can't use cljs or elm because our clients are afraid of them.


Why do you let your clients make your technological decisions?


Because they are paying me and therefore it's not my decision. I give advice but if the client insists on something that's what they get.


I've built a lot of apps with React/Redux, both complex and simple ones.

At first there were lots of boilerplate code, and lots of kind-of-similar-but-different code all around. After each project, I extracted and generalised the helper components/methods/systems to create my own 'framework', and now I have a foundation that eliminates most of the pain points that people experience with react/redux, that's specific to the way I develop software. Thats why I love React and surrounding ecosystem, it allowed me to choose the relevant libraries/solutions that fits my workflow.

Here is my thoughts about the points in the article:

1. Reacters load data on mount

In my components, I just declare the data that it needs, and the relevant system loads the data and sends it to the component via props. (like Relay) No requests happen in the component directly. When you have only a few components that needs data in your app, fetching it in the component works, but when you have a complex app (I build ERP systems) you need a centralised way to handle data fetching.

2. Reacters register a bunch of event handlers in their components

Declarative event-binding works great for me (similar to react-event-listener[0]). Instead of adding the subscribe/cleanup logic to every component, I just declare the events I'm interested in, and just add the relevant handling code to my component.

3. Reacters optimize when to render

Using redux and never overwriting the store takes care of this for me.

4. Reacters load data when stuff changes

Same as 1., yon can declaratively state what you need, and the relevant system can handle the rest of the logic without changing anything in the component.

5. Reacters worry about when to set state

I'm not sure if this is really a problem, especially when you learn how the lifecycle hooks work.

6. Reacters use a lot of Redux boilerplate

Redux has a goal to keep things simple, and let users choose the relevant 3rd party solution to your needs. It means that you need to do some work to create a no-boilerplate solution, but in the end it will work flawlessly according to your needs.

In my 'framework', I generate the store definitions and action handlers for my database models automatically from SqlAlchemy model definitions, thus eliminating 95% of the boilerplate.

I know that there are frameworks out there that eliminates the need to create your own solutions to most of these problems, but I've found out that, when you try to go out of the 'blessed' path, you will be banging your head to the wall more times than you want. With building my own solutions around the react/redux, I'm in control of the things that I care about. When you control the 'magic' part of the stack you use, you have a lot of power in your hands when problems arise.

[0]: https://github.com/oliviertassinari/react-event-listener


What about setting a value deeply nested in your state when you change something in a deeply nested component? What if that value is inside an array?


Only the portion that changes will be rerendered because of the way Clojure shares nested data structures when there are changes.


Not quite: while Clojure does share nested data structures, and that does allow for some neat tricks around fast comparison operations, the fact that only changed SUV trees rerender is all due to react, and its shouldComponentUpdate. A normal js app can get the same results, if it wants.


How would this work in re-frame:

Let's say I have a box with 1000 items. It will take some time to load, so those 1000 items should not be painted in one go (freezing the UI), but instead be shown incrementally, from top to bottom.

Imagine that while it is loading, I can click a "delete" button on one of the items, so that item would disappear while the list is still growing at the bottom.


You create a virtual list, only rendering the items visible in the viewport. Use intersection observer or scroll events to find out when you're about to go past the selected item.


Doesn't that cause either stuttering, or flickering in the viewport?


When I did this back before ajax was a thing, it was by using image elements that were moved like a treadmill, we managed those so that there was a buffer outside the screen area that was pre-filled to avoid stuttering. Essentially we rendered ~1.5 screens wide / long and would fill them in a non-blocking way when the buffer was reduced.


But when you click the scrollbar and drag it down, you can easily make the viewport scroll by more than 2x its height in a matter of milliseconds.

I experimented with this myself, and the UI designer I worked with thought it was not pretty when the screen turned blank while scrolling rapidly.

The problem I had was that painting the items in the viewport took already more time than the allotted time per frame.


Some implementations provide placeholders that are quick to render (e.g. a single static image, no deep DOM/view tree) to work around this kind of thing. In my experience on both android and web it tends to work pretty well - even very low-end hardware can do this quickly enough to be invisible, and if you are able to drop down into something that can do instanced rendering it's about as close to "free" as possible.

Whether the placeholders look any good is a different issue. We generally thought they looked / transitioned to real content a lot worse than showing nothing, and eventually just ripped them out. But e.g. Pinterest did it fairly successfully for a long time (they tended to replace images with a solid image-background-color so the content just kinda emerged. neat effect, but works far better with product images than user-generated content :) )

But ultimately, when you exceed your tolerances, you have to render something or nothing, or disallow it (e.g. block everything). You just have to choose which one. The point where you exceed will shift depending on what you're using/doing, but it always exists.


This problem exists on native apps as well. There's multiple strategies for handling that, depending on your constraints and requirements.

The simplest solution might be to use a placeholder. You might also want to track how much time has passed, so you can stay at 60FPS.


Well, we fixed that by not having a scrollbar! You had to click-and-drag to move the viewport, and thus it was difficult to move it more than ~1 screen width at a time.


This means the code has issues. I have virtualized data grids holding thousands of records with each cell incorporating d3 visualizations and this does not happen.


Do you have a link you can share?


react-virtualized [0] is a great implementation of a virtualized list using ReactJS.

[0] https://bvaughn.github.io/react-virtualized/#/components/Lis...


It depends on your implementation. This isn't a new problem, it also exists in native applications.

If it takes a long time to render each item, you might consider using a placeholder and caching existing elements. Be sure to track how much time is spent rendering so you run at 60FPS.


I agree that reactjs would have been better without setstate, instead with tightly integrated Redux, which would remove much of the Redux boilerplate.

For the most part, this is what this reframe thing aims at as far as I can tell.

Setstate really should be long term deprecated, it's a design error in react.


No, it's really about knowing what is global and what is local. You have plenty of things for which local state makes sense.


True, but ideally the framework would lead programmers automatically to ways of doing things in the "right way" instead of overusing local state.

A "better react" would make it natural and obvious to use store versus local state in the appropriate way.


Well, that's not really the idea behind React. You could say local storage is more a convenience than anything, React is primarily about rendering your data.

The problem is that, with Redux at least, composing data stores is a major pain. The only pattern supported out-of-the-box is horizontal slicing (have different reducer for separate pieces of data). This may work for separate pages, but it does nothing for you as soon as you need data sharing. I don't think React itself should change, but I haven't found a really satisfactory solution to the problem of sharing stored data.


setState has its uses, especially for things like "time from now" displays where the model doesn't change but the display needs to.


I agree, and try to write all my react without it, using redux, thunks and reselect heavily.

I think it's easy to say it was a design error with hindsight. I'm happy React came along, and in the future it will either get better or we'll move on to something else, heaving learnt from it's mistakes.


My theory is that any technology that can be simpler will be replaced by one that IS simpler.

New technologies like react either need to fix their problems like lack of integrated state management or be replaced by something that does do it right.

The criticisms of the linked post are valid.


I read your 2nd sentence as "the view library needs to become a framework".

Integrated state mgmt is out of scope for React JS per se. Some ppl feel mobx is a better approach than redux. They're free to mix n match libs to suit their prefs. The React ecosystem is a set of libraries; by definition that entails more choices, more "fatigue", more combinations of approaches, and more innovation, compared to all-inclusive frameworks like Angular. Don't get me wrong; Ng gets some things right, and there are valid reasons to deliberately limit choices and go all-in w/ a framework. But I respectfully disagree thst React "needs" to integrate state management.


> My theory is that any technology that can be simpler will be replaced by one that IS simpler.

This theory is correct. It explains why virtually nobody ever uses C, let alone C++, and all of our internet infrastructure is built on top of Scheme.


This flies in the face of the proliferation if js frameworks.

Mayhap each simplified something, but the result is more complex. Frighteningly so.


That's a symptom of people not understanding the concept of complexity and falling victim to the law of leaky abstractions.

People don't work towards reducing complexity in large applications (which involves trimming dependencies, removing api surface area and minimizing interactions between different people's code, which is all hard), they work towards reducing complexity in simple toy applications (which is about cramming as many tools in until you can do exactly one specific thing in one short line and it looks nice).

That has the effect of reducing complexity at first glance, but increasing it in real world applications.


I was playing with that thought in my head. Would be neat to highlight some examples of good simplifications.

Databases come to mind. Collections libraries do not. :(


Totally disagree about `setState`. Even with redux it still is very useful.


How do Re-framers handle components that wants to redraw using a timer, such as an animation or progress bar?


you would normally start setInterval/whatever function somewhere and you dispatch the change to re-frame when needed. Reframe will update the db and the components subscribing to that data will redraw when a change occurs. The component(s) themselves have no coupling whatsoever to what is providing the displayed data.


Maybe it'd be like Elm where you can subscribe to external events, as if you were to do something like this:

requestAnimationFrame(() => dispatch('TICK', Date.now() - prevTick))

Then you can feed the offset into view functions which forces a re-render and steps the animation.


'componentDidMount' doesn't exist anymore in React es6.


It still does (these docs use the ES6 class API): https://facebook.github.io/react/docs/react-component.html#c...




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: