Hacker News new | past | comments | ask | show | jobs | submit login
Progressive React (houssein.me)
147 points by brandonhall on Dec 20, 2019 | hide | past | favorite | 84 comments



"Performance advocate" is a thing now? Things like that or "growth hacker" make me long for the peaceful days of "ruby rockstars" and "javascript ninjas"...

Never mind the fact that we're plowing the depths of functional programming, complex data structures and asynchronous wizardry just to make sure that displaying a few bytes of text and flat-shaded borders isn't too slow.

I don't remember GUI programming being that hard in the years before this. At least for these rather simple results. Getting a full DTP platform out on a 20 mhz machine with 4 MB of RAM required some advanced pattern wizardry, but most web UI patterns aren't that complicated. They just aren't helped by the client-server nature and Javascript being Javascript, as much as you wrap it up in the Emperor's new C# clothes.

> Sites have become bigger, more interactive and more complex and this number is still gradually increasing in an upward trend year after year.

Yeah, but is it useful complexity? The whole frontend scene often reeks of "bullshit job" syndrome. To get results customers could live without in a framework not re ally suited for it, we're developing complex tools, even more complex tools to package and deploy them and then have more coaches, trainers, video walkthrough creators and premature optimization providers than ever before.


It's funny because scrolling in this article is a stuttery mess (Firefox on Linux).


Yeah, this made my whole browser lag (buttons-shading-five-seconds-after-hover-level lag).

FireFox Quantum on Windows 10 with a pretty badass CPU. Also slow in Opera. Also slow in Vivaldi.

And it crashed Chrome Canary.

For a web-tech post, this is pretty fucking bad.


Works fine for me in Chrome stable on Linux, Mac, Windows and Safari iOS, Mac.

I also tried scrolling a number of different ways - with autoscrolling, mousewheel, page up/down and arrow keys.

All of the machines I tried it on are 4-5 years old except for my iPhone which came out in 2017.


Works fine in Firefox in iOS...


Works fine in Firefox on Deepin as well.


Ironic indeed, but then the blog itself doesn't use React, the slowdown most likely comes from some utility library[1]. The advice in the article may not be discredited, but this shows that learning basic profiling should come before obsessing over framework-specific optimizations.

[1]: https://github.com/aFarkas/lazysizes


Crashed my phone... (Chrome on Android, though pretty old Sony Experia)


Repeatedly crashed Chrome on my Pixel 3, even when I tried to close the tab. Had to switch tabs, then close it, to get it out of that little loop.


Same here with a Vernee Mix2


For anyone else suffering stuttering, a reminder that most browsers support reader mode which makes it silky smooth again. For Firefox its the page icon in the navbar.


I forgot about it for so long but I use it more and more lately.

It works great.


It is a stuttery mess in Firefox + Windows 10 also.


Stuttery mess on Chrome on Android as well.


In fact, crashes Chrome on my Pixel 3.


It crashes Firefox on my pixel 3. Funny.


It gets better: disable JS and read the article without the stuttering. I am a performance advocate, too!


It works great in Chrome on my 2019 MBP over commercial high speed internet!


FWIW I have a $150 crap phone and the mobile version, while looking a little scruffy, loaded in 3 seconds and scrolled fast to the bottom no problem.


I think it's all those videos/gif in autoplay.


Nah, its throwing a ton of Promise errors.


Works fine on safari iOS iPhone 6


As someone who has web apps in production written in backbone.js, Angular, and React, I can say selectors with Reselect to transform all the data being passed into props, Sagas to manage all async workflows, and Ramda with Redux reducers is pure fire. There is no business logic in components or containers unless it is tied directly to the view and layout, not for the data. It is such an easy way to reason about huge amounts of data coming into the system from lots of different places. For performance, everything gets memoized based on object references. Using the immutable data structures in the store, Reselect keeps the transformed data cached with memoization until the object reference is changed in the store.


Only problem with Redux is it can be misused quite easily. The codebase at work has performance issues because of Redux, rerender issues because of reselect (not confirmed yet) and imo very very hard to read code because of Redux-Thunk. The state management was implemented by one person and everyone else is terrified of touching that part of the codebase.

Im not blaming redux for this, this is clearly a problem with how we implemented redux. But our team of 3 devs would be much much better off by having used Mobx.

I've seen better implementations of Redux and https://github.com/isubasinghe/advanced-redux-patterns is one of them (this is code by Nir Kaufman for his advanced redux patterns talk)


Hi, I'm a Redux maintainer. If you've got any specific concerns I can help with, I'd be happy to try to offer advice.

Out of curiosity, what issues have you been seeing with using thunks?

I'd specifically encourage you to check out our new official Redux Toolkit package. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once:

https://redux-toolkit.js.org

You might also want to look at the new "Style Guide" page we've added to the docs, which lists our recommended patterns and practices for using Redux:

https://redux.js.org/style-guide/style-guide


Hey thanks for reaching out, that's very nice of you :), I have no specific concerns atm but will reach out I do need help.

Issues with thunks are maybe(?) due to our implementation again, but we have way too many dispatches being called (100s of lines of dispatch calls for one action sometimes). It becomes quite hard to trace what happens when a certain business logic is executed because of this.

For example, say we have an action to delete a domain level entity, lets say this is a book, well books can have paragraphs, paragraphs can have sentences and sentences can have words (normalized state, terrible example I know, but all I could think of atm). Well the action to delete a book, has dispatches to delete all children entities as well. These dispatches actually may have other dispatches as well for ui stuff etc. Doing redux this way really didn't scale well for us, but I suppose we have very very unique, complex business logic compared to other projects.

The style guide looks excellent, I think this would have made the codebase easier to follow. I will defs be following this for my personal projects.

Thanks


Yeah, it sounds like the issue here is that you are trying to dispatch a single action per tiny state update. This is why we now recommend "modeling actions as 'events' instead of 'setters'". See the links I posted in this sibling comment for suggestions on how to change your conceptual approach:

https://news.ycombinator.com/item?id=21853160

Also, note that our new Redux Toolkit package will at least help simplify your existing logic, and you can begin migrating to it incrementally:

https://redux-toolkit.js.org


Thunks can be an anti-pattern. They encourage multiple dispatches to redux which can leave people designing "set data" style reducers rather than more meaningful ones. Parent comment suggests sagas and that's much better. An action in a component dispatches one thing and the saga can co-ordinate all of the logic.


> "set data" style reducers

I've heard this before but don't really understand it. Most of my reducers are storing and maybe updating data loaded from the server. What's wrong with this pattern?


Our new "Style Guide" docs page gives a recommendation to "model actions as 'events', not 'setters' [0], and there were two recent talks on this topic that go into a lot more detail [1] [2].

[0] https://redux.js.org/style-guide/style-guide/#model-actions-...

[1] https://github.com/dmmulroy/talks/blob/master/event-driven-r...

[2] https://youtu.be/K6OlKeQRCzo?t=2626 / https://rangle.slides.com/yazanalaboudi/deck#/


> "model actions as 'events', not 'setters'

I don't think actions are what they're talking about, but reducers as setters vs reducers with more complex logic. I stumbled against the same thing last week, and rather than duplicating the logic in two reducers I settled on putting the logic in the action and turning both reducers into simple setters.


That's actually kind of the point.

When you mentally model an action as a "setter", like `SET_PIZZAS_ORDERED`, the reducer usually has almost no logic and just blindly accepts whatever value was in the action. The work of calculating the value was done before the action was dispatched.

If you model actions conceptually as "events", the corollary is that the work of calculating the new state typically ends up in the reducer.


Again no, these are modeled as events around a thunk: dispatch START_SEARCH, -> ajax -> any necessary complex logic -> dispatch RESULTS_RECEIVED or QUERY_FAILED. Multiple reducers listen to the same events, acting as simple setters.


I'm still new to redux and I'm learning using angular ngrx. I realize ngrx is just inspired by redux. What's the team relationship? Are these the same maintainers, is there a ngrx toolkit?


No, the Redux and NgRX teams are completely separate. We've occasionally chatted briefly online, but that's it.


I’m mostly with you there but I’ve found saga to be a double-edged sword. The library offers some KILLER features and when you’ve got the right use case, it’s perfect, but it’s so easy to abuse. Because it does so much, I found myself putting more and more responsibilities on it and wound up with some very magical feeling, hard to troubleshoot code.

What changed everything for me was adding GraphQL and getting the majority of my API communication and data storage out of redux. Now, sagas and redux are further down the list of tools I reach for, complexity is way down across the board, and the sagas and reducers that’s remain are more narrowly scoped and easier to reason about and maintain.


I'm a Redux maintainer. Sagas are a great power tool, but most apps don't need them [0]. We recommend that most apps stick to thunks as the default [1], and our new official Redux Toolkit package [2] adds thunks automatically to your store setup.

I've used sagas in a couple apps that truly did have very complex async workflows, and they were great for that use case. But yeah, using sagas _just_ for something like data fetching is definitely overkill.

[0] https://redux.js.org/faq/actions#what-async-middleware-shoul...

[1] https://redux.js.org/style-guide/style-guide/#use-thunks-for...

[2] https://redux-toolkit.js.org


As someone that had inherited a redux-thunks project, for me thunks are like calling fetch() directly inside your components callbacks, with extra steps. Really bad for testing and isolating code.

I haven't tried saga, just rxjs with redux which was definitely good. I've also worked with Elm, which supposedly inspired redux. Elm does it perfectly, I don't know why Redux missed async.


> I'm a Redux maintainer. Sagas are a great power tool, but most apps don't need them

I’m super happy that you’re here saying that. I’d go farther than “most apps” to “almost no apps” but, though I’ve mostly worked with apps where I wish they hadn’t used sagas, I have a lot of admiration for the saga concept and library.


> I found myself putting more and more responsibilities on it and wound up with some very magical feeling, hard to troubleshoot code

Agreed. There's a simplicity to React / Redux that I absolutely adore. There's very little magic going on: things happen because you've explicitly told them to. Side-effects in Redux seem to throw this out the window.


reselect has been magnificent to us (along with moving most fetching logic to thunks)

API shapes change and evolve, and mapping raw API shapes to components (through Redux) became a major PITA, real fast.

reselect solved all of that; we transform almost all data to a “view layer friendly” format before passing it into the components, keeping them as dumb as possible. We also moved most “view business logic” to the selector level: The check might involve user permissions, subscription plan, etc, etc, but the component only receives ‘shouldThisThingBeDisplayed‘. API shape/changes very rarely impact the components.

This approach makes it much easier to test in our experience. Business logic is tested without any concern for the components themselves. Visuals are covered by Storybook and ChromaticQA.


I could never get into the whole "side-effects for async code" pattern so many people are using with Redux. At my current gig, we are using redux-observable, and that's a WHOLE lot for people to grok to simply ... `await fetch('/users')`. I do agree, redux-sagas is much more in line with what I would expect.

But they all lead to things I would consider "hacky". Since it's all side effects, making sure two things that are "side effects" of the one action run in consistent order can be troublesome, since it's usually down to "well which one registered with the middleware first?"

And, if you ever need to actually simply `await` an action, you need to set up some middleware that will give you a promise that resolves / rejects when certain actions happen. Then you need to tie metadata to your actions if it stands that multiple of the action you're waiting to resolve happen.

Or, some people recommend creating a promise before dispatching, passing that promise reference around, checking for it in your sagas / observable.

All things I would consider super hacky.

I've been using my own middleware for years that simply lets you make `payload` a function, async function, or promise, and it's all incredibly more stable when things get complicated, and newcomers can grok whats going on in a second. You get `/start`, `/success`, `/error` actions baked in. And since it's promise-based, you can `await dispatch()` when you need to. If you need to get the state, or dispatch more actions, you can, right from the original action.

For times when you truly do want something to be a side effect (read: incredibly rarely because my actions have access to `dispatch` and `getState`), I use a very simple middleware like redux-saga but with standard async / await syntax.

I've been intrigued by people praising these side-effect workflows for some time, but after using redux-observable, I don't think I'll ever go near them again.

Apologies for the rant.


I've had good experiences using a simple combination of lit-html and the sam pattern. I'm not an evangelist yet, I want to try and throw some more complex edge cases at it first but so far, I've had no complaints.

1. https://lit-html.polymer-project.org

2. https://sam.js.org


Shout out to Rematch, it has been the go-to library to ail my redux woes. So much is simplified that it makes things way easier to grok.


The more I deal with it, the more I hate React, and the very idea that websites must require Javascript to just show HTML.


Fear is the path to the dark side. Fear leads to anger. Anger leads to hate. Hate leads to suffering.

Jokes aside, everything has its place. You're still allowed to hate it though. For example, I hate Redux because everyone uses it for every React app and it's got boilerplate up the wazoo.

Just know that if you "hate" things, though, you won't learn as much in life, like I won't learn how to use time travel debugging. Almost anything that gets this popular has at least a few core ideas we should probably learn, or learn from.


I’m not sure this is true. For example, what can I learn from JS that isn’t in better languages? In tech we have this weird situation where many things become popular without good reason. It’s ironic, because a group of people (nerds) who think they are the epitome of rationality are actually the opposite: we make very emotional decisions when we could be entirely rational, as we have access to data other fields do not.


Frontend doesn't always mean using JS. You can use a well-designed, modern language like ClojureScript instead that isn't anything like using JS, yet nevertheless has the same access to the browser.


Well... there are only two standard APIs for the front end: DOM and the web API (browser stuff and HTML5 things). Once comfort with those is achieved you suddenly need so much less code to do radically more ambitious things.

The time saved in both authoring and maintenance pays for itself in learning the standards.


> For example, what can I learn from JS that isn’t in better languages?

I only said "at least a few core ideas" -- not, "at least a few unique core ideas". Sure there are other places you can learn the same things, which of course doesn't mean that JS is now somehow stripped of that value.


The web community has a focus on byte size which is rarely found anywhere else in application development.


Is there a way to cutdown the bloat?


Not in a substantial way. The boilerplate is a consequence of the functional “workaround”: representing a mutation as the result of applying a state transition to a complex state, and then computing what to display as a function of both the current and previous states.

The way to reduce the boilerplate is to use a mutable paradigm, but then you lose the simplifications that the immutable paradigm gives you.


There's a fantastic library called Immer [0] that uses ES6 Proxies to let you write "mutative" update logic that is tracked and turned into a safe immutable update.

We recommend using Immer as the best way to write immutable logic with Redux [1], and our new Redux Toolkit package [2] automatically uses Immer internally to let you write reducers like this:

    const reducer = createReducer(initialState, {
      updateItem(state, action) {
        state.first.second[action.payload.id].fourth = action.payload.value
      }
    })

[0] https://immerjs.github.io/immer/

[1] https://redux.js.org/style-guide/style-guide/#use-immer-for-...

[2] https://redux-toolkit.js.org


Check out lit-html. You write functional style code, but the library does efficient minimal mutations of the dom with no virtual dom, and no dom diffing.


I'd recommend checkout out Redux Toolkit and reading over the docs. Severely cuts down the boilerplate, but still walks you through the general ideas and fundamentals in a great way.


If all you want to do is serve up HTML... you don't pick react.


I've used React as a server-side templating language on many projects small and large. The main feature that makes React great for rendering HTML is higher-order components. In my experience any project of sufficient complexity starts to get ugly with a "regular" templating language. React encourages composition of small isolated components, which tends to result in code that is easier to understand and maintain.

Admittedly this works best on projects that don't require a massively complex interactive front-end, like news sites[0] and data reporting tools. But at some point you can just choose to start hydrating the interactive components in the browser without changing your back-end setup at all.

[0]: https://wildlyinaccurate.com/introducing-a-faster-bbc-news-f...


Yeah that's totally true.

Really everything is delivering HTML ... and maybe something else.

I was thinking in the simplest form as the person I was responding to seemed to be saying that just to do something ultra basic react was not something they liked.


Yeah, now if only people actually did that.


React isn't used for webSITES. It's used for web applications.


Hah, I wish.


It's a choice really.

There's ways to do a lot of things (I swear that is the real sticking point on HN when it comes to react discussions).

But just serving up straight HTML, you could actually use react, but you don't have to do it "just" to do that.

If you're stuck in a spot where you HAVE to use it, that's the fault of whomever decided that, not react.


Start by making your blog not crash android chrome.


Crashes mobile Firefox too. Very ironic given the small portion of the article I read before it crashed.


Seriously it kills chrome on Android for me like nothing I've seen since those pop ups that you couldn't escape were popular.


Visiting that page on the Pixel 3aXL and it literally crashed while scrolling. I've never had that happen once in the last few years. Damn.


Is that site crashing anyone else's browser?

I'm using chrome on Android and it kills my browser every time.


It's crashing Samsung Internet on my Pixel 3.


So progressive that the minute I open the page, it crashes. Have no idea why that is.


Crashes mobile Chrome.


I know this is a low quality comment, but given I was once the guy at my last company who's job it was to make React do things it didn't want to, I really dislike React. It's a dead end for interactive UIs, and the closer you can stay to native html elements with encapsulation, the better off you'll be later down the road.


You can use native html elements with react. I'm curious what it is you were trying to do since I'm sure you could do anything native html supports with react.


React is best suited for interactive ui. For static sites just html/css should be fine.


I actually like to use react for static sites too but mostly because my main work is in react and I like to maintain one mental model for everything.


How do you figure?


I call this the 'Rails Paradox'... a lot of people invested into Rails and wrote a lot of software that powers small businesses.

As people exited Rails, the cost of maintenance has gone up... since you have to pay a lot to get someone writing Rails now.


I do think that React has a chance at beating this trend though. As Rails became more and more esoteric with DSLs, utility functions, and `method_missing` hacks that make complex existing monolith codebases nearly inscrutable, and microservices became a preferred way to "throw bodies" at similarly complex codebases, it's not surprising that people started moving away.

React's ecosystem, on the other hand, has embraced strong typing (and alongside it, tooling that lets you drill deep into any method you see on the screen), and it's fully compatible with teams working on discrete backend and frontend components. It's not likely to disappear any time soon, the same way jQuery hasn't disappeared. Yes, it adds a performance cost, but so long as Zawinski's Law holds [0], functional state-to-display-object transformations will continue to be the paradigm, and React continues to lead the pack there. And if new display object abstractions come into play (say, React Native in-browser rendering widgets on the GPU directly via webasm or new browser-specific APIs), React can and will adapt.

Just my 2c.

[0] https://medium.com/programming-philosophy/zawinskis-law-2090...


If it weren't for Rails then maybe those small businesses would not be as successful as they are and wouldn't be around now.

Rails is definitely more niche but it's still a great framework IMHO and it's not dead. Still gets feature updates to this day.


Hope it is not dead. I still haven't taken the time to learn it yet.


It's not bad, the ecosystem makes it good.


Rails salaries are lower than .net where I live. Where do they command $$$?


Bigger parade, bigger cleanup.


Wrong thread?




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

Search: