[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)