[HN Gopher] PSA: React 18 calls code twice in strict dev mode to...
___________________________________________________________________
PSA: React 18 calls code twice in strict dev mode to detect side
effects
Author : tomduncalf
Score : 82 points
Date : 2022-04-10 11:03 UTC (11 hours ago)
(HTM) web link (reactjs.org)
(TXT) w3m dump (reactjs.org)
| DustinBrett wrote:
| new-strict-mode-behaviors
|
| https://reactjs.org/blog/2022/03/29/react-v18.html#new-stric...
| cpeterso wrote:
| To further restrict these component methods from having side
| effects, could React call these methods using a Proxy or eval()
| context that blocks these methods from reading or setting
| variables in the global context?
| brundolf wrote:
| Would be extremely hard, especially given we're not just
| talking about core APIs like fetch: React code uses closures
| like crazy, and you couldn't wrap and probably couldn't eval-
| shadow every single object in scope that might get mutated
|
| You might be able to catch some common cases, but that might
| give a false sense of security
|
| Enforcing against side-effects in JavaScript is pretty much a
| lost cause. Even merely accessing a property can cause side-
| effects, thanks to getters and proxies
| bob1029 wrote:
| Blazor does something like this but for different reasons
| (ServerPrerendered mode for lower latency UX).
|
| It took me a solid day to realize the behavior was intentional.
| There were no problems but it was one of those "I cannot build an
| empire on these uncertain foundations" moments.
| tomduncalf wrote:
| I thought I'd share this in the hope that other developers see it
| and don't lose a bunch of time wondering why e.g. code in a
| useEffect(..., []) block is being called twice.
|
| I created a new Next.js project and was very confused to see my
| console.logs appearing twice. It might be nice for React to log a
| warning that it is going to do this as it's quite confusing if
| you aren't aware of it!
| rektide wrote:
| This seems like a super aggressive move, giving up lots of
| performance & complexity for some pretty com ppl lex
| consistency checking.
|
| Is tbis in production build too?
| technion wrote:
| No, this is document as only occurring in development mode.
| tomduncalf wrote:
| Yea sadly there weren't enough characters in the title
| field for me to put "strict dev mode" so that nuance was
| lost
|
| Edit: dang updated the post title to include this
| tomstuart wrote:
| Aside from being development-only, note that you also need to
| wrap a component in
| `<React.StrictMode>...</React.StrictMode>` to opt it into
| this.
| tomduncalf wrote:
| Yeah, good point - I thought it could be that Next.js
| changed their default behaviour to use strict mode when
| they upgraded to React 18 but another comment says React 17
| silenced the duplicate logs(!) so maybe the change is older
| than that.
| beart wrote:
| this sounds similar to angular which performs multiple passes
| in dev mode and warns if values have not settled
| mbell wrote:
| > In React 17, React automatically modifies the console methods
| like console.log() to silence the logs in the second call to
| lifecycle functions. However, it may cause undesired behavior in
| certain cases where a workaround can be used.
|
| I feel like when you've gotten to the point that something like
| this has been proposed and accepted as a good solution to a
| problem your framework is facing, it may be a good time to stop
| and reconsider the approach of the framework.
| nightski wrote:
| It's not a "solution". It's more akin to a lint rule imho.
| Javascript provides no language level guarantees. There is no
| static type checker or compiler to enforce these things. So we
| rely on linters, tests, and assertions. Yay dynamic languages.
| btown wrote:
| This gets at something deeper: React has a policy of "in the
| dev environment anything goes; we will mess with your code and
| its runtime performance to help detect bugs or future bugs; you
| want us to do this, even if you think you don't."
|
| But this assumes good QA and staging environments exist and are
| used correctly. In reality, many users of React test on local
| and immediately deploy into production. The more the
| environments differ, the less friendly React is to these users.
| Timing issues and race conditions may surface in production.
| And thank goodness this isn't Python where a log statement can
| consume an iterable if you allow it to do so!
|
| And for those saying "Strict Mode is opt-in" - your coworker
| may opt in your whole app without you knowing it. Hopefully you
| see the PR or the Slack thread. So much in React now enables
| this "spooky action at a distance."
|
| The React team has put a lot of thought into balancing the
| performance needs of large codebases with the stability needs
| of smaller teams with less robust SDLC. I can't think of a
| better workaround. But it's still going to cause chaos in the
| wild.
| kristaps wrote:
| I agree, at first reading this sounds just plain horrible. I
| haven't tried doing any research, can anyone point out why it's
| not?
| mhoad wrote:
| Broadly speaking I think React is in a weird spot where they
| have painted themselves into a corner with no obvious way of
| fixing it by essentially forking the DOM and doing so many
| things independently of the wider web platform. That approach
| made a lot of sense when it was first released but is just a
| liability at this point.
|
| If you're deep in React land and it's all you know I think 2022
| might be a good time to expand your horizons a bit because the
| landscape around you has changed a lot in the past five years.
| resoluteteeth wrote:
| > Broadly speaking I think React is in a weird spot where
| they have painted themselves into a corner with no obvious
| way of fixing it by essentially forking the DOM and doing so
| many things independently of the wider web platform. That
| approach made a lot of sense when it was first released but
| is just a liability at this point.
|
| The problems that strict mode is addressing with this
| somewhat unusual approach have absolutely nothing to do with
| the VDOM. Rather it is that code using React is subject to
| restrictions (being pure functions and following the hook
| rules when using hooks) that are impossible to express or
| enforce at compile time in javascript.
| mhoad wrote:
| Sorry just to be clear, I wasn't tying this to the strict
| mode situation but was attaching myself to the concept that
| this is just one of several points where the framework is
| showing some signs that it's time to start thinking about
| alternatives particularly because I don't think some of
| them are fixable at this point, they are just too central
| to the entire project and those decisions no longer make
| sense.
| sam0x17 wrote:
| oh god, the horror of the fact that this is necessary! As someone
| who has largely and happily sidestepped the whole reactivity
| thing for the last decade, it really sounds like that whole world
| is starting to implode and contradict the very things it promised
| in the first place (simplicity). Please let web assembly deliver
| us out of this js-ridden mess \o/
| dolni wrote:
| I'm glad I'm not the only one who sees this.
|
| Are the people building React completely oblivious to the nigh-
| unbearable code smell that this is? Why is it so difficult to
| reason about whether or not code will have side effects?
|
| I'm no frontend developer, and certainly don't have experience
| with React, but after stopping in at their homepage to see what
| it does for me and finding out about "JSX syntax"... jesus. I
| don't know how you parse something like JSX, but I would be
| willing to bet the implementation is a dumpster fire of
| completely unnecessary complexity.
|
| I would really like to believe that the people building this
| stuff aren't amateurs, but the more I learn, the more convinced
| I am that this is the case. The modern web is bloated, slow,
| and riddled an assortment of issues that is different depending
| on which site you're on. Will my back button work this time?
| It's anybody's guess!
|
| It only seems appropriate to lay the blame at the feet of those
| who thought the best course of action was to make web
| development ten times more complicated than it needs to be.
| joshribakoff wrote:
| > Are the people building React completely oblivious
|
| Perhaps the people criticizing it so flagrantly are oblivious
| to the goals motivating their design choices.
|
| No one is forcing you to use it.
| eyelidlessness wrote:
| Others are right to point out that this isn't a problem with
| reactivity. But there's an even more important correction here:
| React, perhaps in spite of its name, isn't actually (fully)
| reactive. And while that's sort of an academic nuance[1], it's
| the underlying reason for implementing things like this.
|
| 1: https://dev.to/this-is-learning/how-react-isn-t-reactive-
| and...
| scarmig wrote:
| I'm a skeptic of the promises of reactivity and a fan of web
| assembly, but the main reason reactivity is a pain is because
| UIs are inherently a painful domain, not because of JS itself.
| dolni wrote:
| Web browsers have had callbacks for DOM elements since...
| forever. Why did we need to add an entire component framework
| like React on top of it?
|
| Everything on a web page does not need to be interactive.
| It's a document first.
|
| Web applications are cool. Maybe React makes sense there.
| _But every site does not need to be a web application!_ And
| yet somehow, React and company became the industry default.
| culi wrote:
| React doesn't have to be your whole website. Even the
| starter tutorial in the docs starts off by showing you how
| to just add in React through a CDN and make a react app as
| a single component of a bigger webpage
|
| Obviously that's not at all how it's used in practice (or
| even in the create-react-app template), but I do think
| there's a place for something like React
| eyelidlessness wrote:
| I agree with you to the extent that components =
| interactive. But there are other benefits of a component
| model, even for pages which are mostly static. They allow
| you to develop the same page and its various (reusable)
| parts with the same code for rendering the static parts and
| the dynamic parts. This is especially nice on sites where
| the dynamic-to-static ratio may vary significantly between
| pages, or between areas of the same page.
|
| This use case hasn't been particularly well supported by
| many component libraries (React among them; Marko being a
| long time notable exception), but it's an area that's
| currently having a bit of a renaissance: there is of course
| Marko which has supported this model for years, joined by
| other notable frameworks like Astro and Qwik. Even React is
| moving more in this direction with Server Components, just
| targeting other use cases.
|
| The component model will continue to thrive, not because
| everything is interactive, but because components are an
| excellent abstraction for developing for the web (and a lot
| of other formats).
| dham wrote:
| > UIs are inherently a painful domain
|
| No they aren't. Developers think their a painful domain
| because they are the ones that introduce the complexity in
| the name of the customer who doesn't care, to increase the
| developers salary in some kind of crazy Ponzi scheme.
| steve_adams_86 wrote:
| I've been working on UIs for 15 years and when I read
| comments like this I have to wonder if you've worked on
| complex, stateful, feature-rich UIs. The potential for bugs
| to appear is almost exponential as you add layers, and it
| isn't really the fault of the developer so much as the
| nature of nested behaviours. This is true in any
| environment, but easier to manage when human beings aren't
| the ones executing your code in non-deterministic ways in
| variable execution settings.
|
| The tools we have in the browser are excellent up to a
| point. I think the challenge comes when you need to create
| reusable abstractions around them several layers deep.
|
| There are tools which help manage these challenges really
| well, like state charts, but they aren't perfect and they
| come with their own challenges. The reality (I think) is
| that UIs can present quite incredible complexity, and
| creating tools with approachable APIs to help tame the
| complexity is a legitimately huge challenge.
|
| It could be that I'm a bad software developer (most days
| I'm pretty sure there's some truth in that), but I'm
| passionate and spend a lot of time deep in this space and
| exploring ways to make it better. It isn't rocket science,
| but it isn't digging holes either. The react team is a
| bunch of smart people and they're doing good work on
| solving a tough challenge.
|
| I don't see a Ponzi scheme at all. Sorry if I've missed
| some sarcasm or something here - feel free to disregard if
| that's the case (or if it isn't, too)
| akvadrako wrote:
| It's not reactivity that's the problem, it's React. The way
| svelte and solid.js do it is simpler. Things only run once
| unless explicitly made reactive. And there is no virtual DOM.
| systemvoltage wrote:
| Unpopular opinion: I took a look at Svelte. Was immediately
| disappointed that I can't just drop it in an existing
| website.
|
| All these frameworks assume you're writing stuff from
| scratch. Most commercial software, both internal and external
| facing, have legacy to deal with.
|
| Even with a brand new app, you have to succumb to
| npm/webpack/babel stuff. It feels like building a castle on
| stilts. Inspires confidence, it does not. Back to jQuery I
| guess.
| jasonlotito wrote:
| So, how would you, in JS, go about ensuring the code people are
| writing is side-effect free? Specifically.
| TAKEMYMONEY wrote:
| > This only applies to development mode.
| tomduncalf wrote:
| dang was kind enough to update the post title for me to make
| this clear!
| dataangel wrote:
| If they don't want people to just end up depending on this
| behavior in a stateful way instead they should actually
| deliberately rely on non-determinism, call the functions a small
| random number of times.
| dymk wrote:
| It's not meant to thwart adversarial cases or situations where
| programmers are trying to "trick" strict mode. It's a safety
| net for those who know the "rules" of react, but still might
| accidentally introduce side effects and want to catch it.
| brundolf wrote:
| Even just as a safety-net, there's precedent for this. Golang
| intentionally randomizes hashmap key iteration order to
| prevent people from relying on it:
| https://stackoverflow.com/questions/9619479/go-what-
| determin...
| malkia wrote:
| That sounds really reactionary!
| [deleted]
| leros wrote:
| Well that explains a lot of things.
| azangru wrote:
| Um, I remember StrictMode was running the code twice since
| forever. Probably since the time it was introduced in React
| 16-something. Has anything changed in React 18?
| franciscop wrote:
| Yes, they used suppressed the double logging with the double
| render, but now in 18 they show the second logging as well, but
| with dimmed colors:
|
| https://twitter.com/dan_abramov/status/1507750167390400512
| thomasfromcdnjs wrote:
| Pontificating a little here, but it reminds me of "The road to
| hell is paved with good intentions" quote.
|
| Any external code that purports to be reusable should not be
| concerned with the idea of an "environment".
|
| Positing that reducers should be pure is fine and dandy, telling
| me I can't throw in a side effect is none of your business.
|
| Instead of running it twice, just make a warning log and be done
| with it.
| arcbyte wrote:
| With React, it's your code that is the external code. Your
| reason is quite sound. Your reducers shouldn't be concerned
| with any jind of "environment" or side effects.
| brundolf wrote:
| It can't log a warning because it can't actually detect side-
| effects. The only way to "detect" them is to push them to cause
| breakage in the app logic
| the8472 wrote:
| > Positing that reducers should be pure is fine and dandy,
| telling me I can't throw in a side effect is none of your
| business.
|
| API contracts work in both ways. The user has to uphold it just
| as much as the provider. If you don't breakage ensues. In
| lower-level languages this can include the nasal demons from
| UB.
| Gravey wrote:
| Afaik this has been the behavior of StrictMode since it was
| introduced in React 16.
|
| One thing that's called out in the linked docs is that duplicate
| console.logs were _deliberately_ silenced in React 17. I have no
| idea what they were thinking when they made this decision, but it
| sounds like it has been walked back in 18.
| supermatt wrote:
| It was like this even before strict mode was introduced.
| jitl wrote:
| React also already ran components twice when devtools is open,
| even without strict mode. This happens in my React 16 codebase
| with silenced console.log and no StrictMode in sight.
| joshribakoff wrote:
| I think you mean it logged log messages twice?
|
| I don't believe having devtools opens changes the runtime
| semantics of the app.that would defeat the point of the
| devtools.
|
| I do know it annoyingly logs things twice
| lioeters wrote:
| Monkey-patching console.log sounds so hacky..
|
| > In React 17, React automatically modifies the console methods
| like console.log() to silence the logs in the second call to
| lifecycle functions. However, it may cause undesired behavior
| in certain cases where a workaround can be used.
|
| > Starting from React 18, React does not suppress any logs.
| However, if you have React DevTools installed, the logs from
| the second call will appear slightly dimmed. React DevTools
| also offers a setting (off by default) to suppress them
| completely.
| marcus_cemes wrote:
| Also, these types of global modifications make such libraries
| fundamentally incompatible with projects such as Tauri that
| focuse on safety and security. In a recent video of theirs
| they mentioned that of the popular frameworks, the only one
| that didn't seem to do any dodgy modifications of the global
| scope was Svelte (they didn't mention which frameworks failed
| the test, but I imagine React was one of them).
| joshribakoff wrote:
| It's totally opt in, and in development only, so react
| doesn't prevent you from using that other lib
| SirHound wrote:
| It is, I have lost tens of hours to this over the last couple
| years.
| joshribakoff wrote:
| The hacker news post doesn't even link to the article about
| what's new in react 18:
| https://reactjs.org/blog/2022/03/29/react-v18.html#new-stric...
|
| > To help surface these issues, React 18 introduces a new
| development-only check to Strict Mode. This new check will
| automatically unmount and remount every component, whenever a
| component mounts for the first time, restoring the previous
| state on the second mount.
| SirHound wrote:
| It used to call render twice, now it runs effects twice also.
| tomduncalf wrote:
| Interesting and an odd choice of behaviour in 17! I tested in a
| codesandbox with Next.js using React 17 and 18 and observed
| that only 18 did the double logging I was seeing, so assumed it
| was new behaviour in 18. Actually I guess I was seeing 17
| silencing the double log.
| supermatt wrote:
| Pretty sure its been doing this in dev mode since 0.14. Maybe
| earlier...
| joshribakoff wrote:
| the hacker news title just isn't precise about what part of it
| changed and doesn't even link to the relevant article
|
| https://reactjs.org/blog/2022/03/29/react-v18.html#new-stric...
| tomduncalf wrote:
| Yeah I think what might be going on is a combination of Next.js
| switching to strict mode at some point, and React 17 hiding the
| double logs.
|
| I'd never seen the behaviour before (but had never explicitly
| used strict mode - to be honest I didn't know it existed!) and
| switching between React 17 and 18 was enough to make it happen,
| so I assumed that was the responsible change, but actually it's
| a bit more subtle.
|
| Thought it might be useful to share for people who are using
| Next.js anyway, as for me it just started happening when I
| created a new project with no explanation which led to me
| thinking I was losing it for a while, lol.
| marcus_cemes wrote:
| If anyone is curious as to why, I believe it's the only way that
| React can "poke" your code to see whether it really is side-
| effect free, and therefore concurrent mode/suspense compatible.
| The idea is that React can re-render your component whenever and
| however often it likes whilst getting the same results for the
| same input state, without this assumption, new patterns such as
| concurrent mode and suspense become very difficult to implement.
| Double rendering is a rudimentary dev-time test to see whether
| the output is actually the same to try and help you avoid bugs
| later on (data races for example), without enforcing a language,
| such as ReasonML or Elm. Alternatives would require really
| complex static analysis or strict ESLint rules. There may be
| other reasons as well.
|
| Sometimes there's a need for side effects, the DOM is very
| stateful and unfortunately React is not quite fast enough for
| smooth animations at 60fps on most devices, it's also a lot
| easier to use global variables (no judgement) rather than
| properly organising state hooks and component props.
|
| React started to move away from OOP and more towards FP, starting
| with hooks and function components. One of the driving reasons
| behind this was that function components could be minified a lot
| better than classes. Reducers, side effects, pure, memo, are
| terms that are more known in the FP land. I also think it gave
| them a lot more control as a framework to implement patterns such
| as concurrent mode as functions are stateless unlike classes,
| moving the state up into the React framework itself. This means
| several versions of the state can exist at once, and React just
| runs them through your function to generate the view. Side
| effects introduce unpredictability and nondeterminism using this
| pattern.
|
| This FP style can be confusing to new programmers (anyone feel
| like trying to explain useEffect() to a 5-year-old? Anyone?),
| especially those that are used to global variables or
| manipulating the DOM directly (JQuery), but understanding the OOP
| render cycle had its own learning curve. Frameworks such as Elm
| are met with much love, even if React's hook-based approach is a
| little different.
|
| Small aside, I also think that's where useEffect() got it's name
| from: side effects. It's where you put all those nasty network
| calls and setTimeout()s, and then scratch your head when you
| forgot to cancel/unregister them when the component unmounts and
| React complains that you tried to change the state on an
| unmounted componment. Suspense (hence the need for side-effect
| free functions, hence this double-rendering problem) _should_
| help with avoiding putting "if (stillMounted) { ... }"
| everywhere in useEffects with async code by giving React more
| control over the render/update process.
|
| Full disclaimer, I haven't actually written React in a while, not
| since I found out about Svelte, I'm curious, how to authors of
| libraries such as React/Framer Motion handle the double rendering
| issue when interacting with the DOM directly? Other frameworks
| that I know of that avoid the complexities of concurrent
| mode/suspense either very stricly enforce this FP style, such as
| Elm, or try to be fast enough to forgo the virtual DOM and
| embrace mutable state, events and two-way data binding such as
| Solid and Svelte.
| joshribakoff wrote:
| Excellent synopsis.
|
| > React complains that you tried to change the state on an
| unmounted componment.
|
| They actually got rid of that in the new react :)
|
| > handle the double rendering issue when interacting with the
| DOM directly
|
| I think the idea is whatever they do to the DOM needs to be
| idempotent. Those should be using useLayoutEffect usually.
| petilon wrote:
| > _React started to move away from OOP and more towards FP_
|
| Please don't confuse what you see in React with FP! It is not
| FP. See:
|
| https://mckoder.medium.com/why-react-is-not-functional-b1ed1...
| ulucs wrote:
| Effect, State etc are monads, and 'use' is do-notation that
| lets you use them in an imperative style. What's not
| functional about that?
| shawnz wrote:
| It seems to me like this is just arguing that react is not
| _purely_ functional. That doesn 't disagree with what the
| other poster is saying which is that react is moving towards
| a more functional style.
| petilon wrote:
| > _more functional style_
|
| What does "functional style" mean here? Is C language a
| functional programming language? It lets you write
| functions, pass functions around as parameters, and so on.
| So, is C functional?
| agumonkey wrote:
| never mutate anything and just compose functions and your
| C is functional
| shawnz wrote:
| Without closures it's a stretch, but yes I would say you
| can write in a functional style in basically any
| language.
| petilon wrote:
| I would argue that other than eschewing classes for bare
| functions, there is nothing functional (as in "Functional
| Programming") about React or hooks. Simply removing
| classes and calling it "functional" is a stretch.
| shawnz wrote:
| They didn't call it "functional" though. They simply said
| that it is more functional than it was previously. If you
| reserve the word "functional" for only the most
| idealistic implementations of those ideas then it just
| makes it unnecessarily difficult to describe things which
| are in the middle.
|
| Basically, I think "functional" is more of an axis than
| it is a circle on a Venn diagram.
| petilon wrote:
| > _They didn 't call it "functional" though._
|
| See [1] where they say "if you're a functional
| programming purist and feel uneasy about React relying on
| mutable state as an implementation detail, you might find
| it satisfactory that handling Hooks could be implemented
| in a pure way using algebraic effects".
|
| [1] https://medium.com/@dan_abramov/making-sense-of-
| react-hooks-...
___________________________________________________________________
(page generated 2022-04-10 23:01 UTC)