1

I am currently upgrading my codebase to react 19 and using the compiler. Traditionally in react inline arrow functions in the jsx were considered bad practice for performance reasons, however I am trying to find confirmation on whether the react compiler will handle this use case now and I can safely use inline functions.

e.g previously I may have done something like this:

const testFunction = useCallback(() => {
    doSomething(id)
  }, [id])

  <Button onClick={testFunction} />

but can this now be simplified to:

<Button onClick={() => doSomething(id)} />

without affecting performance.

Does anyone know if the compiler will handle inline arrow function now? I would be especially interested to know from anyone that may have worked on the compiler itself to be able to confirm.

I have done some searching through google and reading the docs but haven't found explicit confirmation on this.

2
  • For the record the useCallback version would not be faster unless Button is memoized and the props didn't change, otherwise it's marginally slower and more memory inefficient. And memoizing a Button probably wouldn't be worth it vs useCallback + memo retaining a copy of props and doing a comparison Commented Apr 25 at 17:31
  • 1
    "Traditionally in react inline arrow functions in the jsx were considered bad practice for performance reasons" - Is/was it though? I seem to recall this idea be prevalent when I first got involved with React, but to be honest I don't think I've ever seen hard evidence proving it. The useCallback hook was always about providing a stable callback prop value and not a performance enhancement. I think "considered a bad practice" was just mis-intentioned premature optimization. Commented Apr 25 at 19:53

1 Answer 1

1

TL;DR you can now write inline handler functions again woot!

Apparently yes the compiler does pull out and cache those inline functions, at least in the simple cases: you can see the click handler being pulled into a top-level const named _temp. There may be some complexities around whether it can do it for e.g. a renderprop function, but those are kinda passe at this point anyway.

Another interesting thing is that instead of useCallback is seems to store the function in some sort of local cache specific to the compiler, see here for a slightly more interesting example. You can see the call to _c(2) for setting up a persisted state across calls to the component function with 2 different entries. Then instead of pulling out the inline function it instead memoizes the entire return value and updates it only when the count changes. This is a somewhat interesting choice vs the other more expected pattern in the simpler case.

Hopefully the links won't go stale but JIC, this:

import { useState } from 'react'
export default function MyApp() {
  const [count, setCount] = useState(0);
  return <div onClick={() => console.log(`count: ${String(count)}`)}>Hello World</div>;
}

is being converted to

import { c as _c } from "react/compiler-runtime";
import { useState } from "react";
export default function MyApp() {
  const $ = _c(2); // <-- constructs the local scope cache
  const [count] = useState(0);
  let t0;
  if ($[0] !== count) {
    t0 = (
      <div onClick={() => console.log(`count: ${String(count)}`)}>
        Hello World
      </div>
    );
    $[0] = count;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  return t0;
}

by the compiler (the playground does not appear to give a version?).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.