React is React, just.

February 17, 2024

I scattered my thoughts on Twitter, so I'll organize them briefly here.

Introduction & Premise

First, as a premise, I am very much in favor of the approach of using a compiler for optimization. This approach is often effective for improving DX without breaking the interface, and I myself use a framework that emphasizes this. However, I have some questions about the mental model changes (or unification 1) that come with the compiler's intervention. I don't write React regularly and I'm not an expert, so what I'm about to discuss is not so much an opinion about React, but rather curiosity about "how do people who use React feel about this point?" — it's not about whether it's right or wrong. I'll say it again: I am in favor.

Also, these are thoughts I had when the React Compiler was mentioned on https://react.dev/blog — this isn't based on thorough research into the discussions that have taken place about the React Compiler. This is just an essay.

I welcome answers to the questions I have, but I don't welcome discussions about whether React Compiler or React itself is good or bad based on this essay. The merits of a technology cannot be evaluated by taking just this portion. (That's what I think, so if there's any statement in this essay that could be taken that way, please point it out. I'm not trying to spread any judgment.)

My position: Organizing thoughts and questions Reader's position: Discussing and responding to thoughts and questions What I'm not doing: Judging the merits of the technology

The reason I went so far to add this disclaimer isn't because I didn't want to be criticized or anything like that — it's because the merits of paradigms vary greatly depending on perspective, and there hasn't been enough discussion to talk about them. I really think it's bad when misconceptions spread about the merits of technology, so I want to avoid that.

Topic

This is already subjective, but there's an assertion to some extent that React is "pure JavaScript." Here, I think we need to be careful because "pure" can have multiple meanings.

Pure means: (i) React Components are idempotent JavaScript (ii) React Components are Just JavaScript

What I was particularly curious about this time was: "With the arrival of React Compiler, it seems like (ii) can no longer be satisfied — how do you feel about that?" This is based on the assertion that both were satisfied before React Compiler came along.

Regarding (i), React Compiler is actually an optimization implementation based on the rule that "components should be idempotent," so naturally this can still be said after React Compiler's arrival. I'll call the semantics being considered in (i) "React semantics."

The problem is (ii). The assertion is that React Compiler doesn't change React semantics, but "it does change JS semantics." I'm interested in how people using React feel about this. That's the topic this time.


About what "it changes JS semantics" means

Let's consider a component like the following.

This is actually code that appeared at React Advanced 2023's talk about React Forget. (Partially omitted)

function VideoTag({ heading, video, filter }) {
  const filteredVideos = [];
  for (const video of videos) {
    if (applyFilter(video, filter)) {
      filteredVideos.push(video);
    }
  }

  if (filteredVideos.length === 0) {
    return <NoVideos />;
  }

  return (
    <>
      <Heading
        heading={heading}
        count={filteredVideos.length}
      />
      <VideoList videos={filterdVideos}>
    </>
  )
}

When React users see code like this, they'll notice the unnecessary computation and optimize it.

function VideoTag({ heading, video, filter }) {
  const filteredVideos = useMemo(() => {
    const filteredVideos = [];
    for (const video of videos) {
      if (applyFilter(video, filter)) {
        filteredVideos.push(video);
      }
    }
    return filteredVideos;
  }, [videos, filters])


  if (filteredVideos.length === 0) {
    return <NoVideos />;
  }

  return ...;
}

Reference: Understanding Idiomatic React – Joe Savona, Mofei Zhang, React Advanced 2023 t=196

However, with the arrival of React Compiler, this is about to change significantly. React Compiler analyzes this code (the one without useMemo) and transforms it into JavaScript code that performs memoization.

function VideoTab(t36) {
  const $ = useMemoCache(12);
  const { heading, videos, filter } = t36;
  let filteredVideos;

  if ($[0] !== videos || $[1] !== filter) {
    filteredVideos = [];

    for (const video of videos) {
      if (applyFilter(video, filter)) {
        filteredVideos.push(video);
      }
    }
    $[0] = videos;
    $[1] = filter;
    $[2] = filteredVideos;

  } else {
    filteredVideos = $[2];
  }

  if (filteredVideos.length === 0) {
    return <NoVideos />;
  }

  return ...;
}

Reference: Understanding Idiomatic React – Joe Savona, Mofei Zhang, React Advanced 2023 (t=497)

This means it's "optimization of JavaScript code (output code) in terms of React semantics," and "JavaScript semantics" itself has changed.

A JavaScript function is something that, when written like:

function Func(n) {
  let list = [];
  for (let i = 0; i < n; i++) {
    list.push(i);
  }
  return list;
}

will always be executed regardless of its result.

But React Components after React Compiler are not like that. On the surface it looks like a JS function, but it's no longer Just a JS function, it seems.

function VideoTag({ heading, video, filter }) {
  const filteredVideos = [];
  for (const video of videos) {
    if (applyFilter(video, filter)) {
      filteredVideos.push(video);
    }
  }

 ...
}

Even though you wrote this, the number of function calls and the number of times the written process actually executes don't match. Such evaluation rules don't exist in JavaScript — they're the result of React Compiler optimizing based on React semantics.

Does this convey what I mean by "with the arrival of React Compiler, React Components are no longer Just JavaScript"?


The semantics have doubled, but the syntax remains one

Setting aside the discussion of whether we should use both "React semantics" and "JS semantics" when actually implementing Components in React,

function VideoTag({ heading, video, filter }) {
  const filteredVideos = [];
  for (const video of videos) {
    if (applyFilter(video, filter)) {
      filteredVideos.push(video);
    }
  }

 ...
}

It's a fact that when looking at this function, two semantics exist. (React semantics and JS semantics) Sometimes the number of function calls and the number of body executions may match, and sometimes they may not. I don't know if this will be a burden for developers or learners, but the possibility isn't zero.

A note about the advantages related to syntax

Until now, I've mainly discussed the semantics of a function like this:

function VideoTag({ heading, video, filter }) {
  const filteredVideos = [];
  for (const video of videos) {
    if (applyFilter(video, filter)) {
      filteredVideos.push(video);
    }
  }

 ...
}

But now about syntax. Regarding syntax, whether before or after React Compiler's arrival, it's undoubtedly "Just JavaScript." (There's also the argument that JSX isn't JS in the first place, but that's not the main point here, so I'll skip it)

One advantage often cited for React being Just JavaScript is the ease of static analysis and integration with toolchains. I personally think these can still be generally claimed. (Whether that means other things are bad is a separate discussion) Strictly speaking, there's a possibility that tools where JS semantics are important may partially not be so, but I feel like there are hardly any tools where that becomes important (completely subjective).

However, what I want to note is that distinction. Before React Compiler, "React is Just JavaScript, therefore ${any evaluation}" could apply to both semantics and syntax. But after React Compiler's arrival, I think this has become a syntax matter.

When someone says "React is Just JavaScript, therefore ${any evaluation}," without this distinction, there's a possibility of making incorrect evaluations. I'm not sure how much of a problem this will be, but I thought this might be a newly raised issue.

So what can we say React Components are?

If it changes JavaScript's semantics, it doesn't seem like we can call it "Just JavaScript." So what is it? . . . . React is React. It's something different that has the same syntax as JavaScript but different meaning.

That's what I thought, but what do you all think...?

Essay: Is React JavaScript + Rules, or is it a language?


ref:

Footnotes

  1. Of course, some people were only thinking in terms of React semantics from the beginning. For them, nothing has particularly changed, but I thought people who were also considering JS semantics would be unified to only think in terms of React semantics (I'm not sure how it actually is)

Back to all posts