Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fast JSX: Don't clone props object #28768

Fast JSX: Don't clone props object #28768

ユウト

April 24, 2024
Tweet

More Decks by ユウト

Other Decks in Programming

Transcript

  1. Agenda 1. 高速化のポイント 2. key, ref の予約語をprops からの削除 3. public

    api であるcreateElement 後のprops の上書き 4. 実装を見る 3
  2. 1. key, ref の予約語をprops から削除すること: key とref key: リスト内の要素を一意に識別するためのプロパティ ref:

    コンポーネントのdom 動作を行うことができるプロパティ // key const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => ( <li key={number.toString()} ref> {number} </li> )); // ref const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); 8
  3. 1. key, ref の予約語をprops から削除すること 問題点 key とref をprops で渡さないようにする必要があった。

    for (propName in config) { if ( hasOwnProperty.call(config, propName) && propName !== 'key' && (enableRefAsProp || propName !== 'ref') ) { if (enableRefAsProp && !disableStringRefs && propName === 'ref') { props.ref = coerceStringRef( config[propName], ReactCurrentOwner.current, type, ); } else { props[propName] = config[propName]; 9
  4. 1. key, ref の予約語をprops から削除すること 解消方法 React19 からは props.ref でref

    にアクセスできるようになるそう 今までは React.forwardRef で囲む必要があった このアップデートによりref の問題は解消できた そしてkey も元々スプレット構文を使わなければクローンはされないようになって いました。 スプレット構文を使わなければjsx 関数の第三匹数に値が入る key の解決 (スプレット構文使用時以外) 10
  5. 2. createElement がpublic api である createElement とは何か React17 以前で使われていたjsx をdom

    に変換するための関数。これは我々開発者が React からimport して使用することもできる // jsx // compiled jsx function Greeting({ name }) { function Greeting({ name }) { return ( return createElement( <h1 className="greeting"> "h1", Hello <i>{name}</i>. Welcome! { className: "greeting" }, </h1> "Hello ", ); createElement("i", null, name), } ". Welcome!", ); } 12
  6. 2. createElement がpublic api である ユーザーによるprops の上書き ユーザーがprops の上書きができてしまうので、予期しないバグが起きる恐れがある。 const

    props = { className: "my-div" }; const element2 = React.createElement("div", props); props.className = "my-div-changed"; console.log(element2.props.className); // 'my-div-changed' 13
  7. 2. createElement がpublic api である React17 以降 createElement ではなくjsx 関数が使われるようになった。(React17

    におけるJSX の新し い変換を理解する) // jsx // compiled jsx function Greeting({ name }) { function Greeting({ name }) { return ( return _jsxs("h1", { <h1 className="greeting"> className: "greeting", Hello <i>{name}</i>. Welcome! children: ["Hello ", _jsx("i", { children: name }), ". Welcome!"] </h1> }); ); } } 14
  8. 2. createElement がpublic api である the new JSX runtime, jsx,

    is not a public API 翻訳: 新しいJSX ランタイムjsx はパブリックAPI ではない jsx 関数はpublic api ではないため、ユーザーによるprops の上書きを考慮する必要がな くなった 15
  9. 2. createElement がpublic api である 疑問点: the new JSX runtime,

    jsx, is not a public API react/jsx-runtime からimport して使えるやーーん import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const Foo = () => { return _jsxs( "div", { children: [ _jsx("p", { id: "a", children: "I am foo" }, void 0), _jsx("p", { children: "I am foo2" }, "b"), ], }, void 0, ); }; 16
  10. 2. createElement がpublic api である 予想: the new JSX runtime,

    jsx, is not a public API jsx のコメントを見た感じは、使えないではなく使ってもこっちは知らないよみたいな ニュアンスなのかなと感じています。 /** * Create a React element. * * You should not use this function directly. Use JSX and a transpiler instead. */ export function jsx( type: React.ElementType, props: unknown, key?: React.Key, ): React.ReactElement; 17
  11. 実装を見てみる 単純にfor 分で回さなくてよくなったので早くなってそう... ?? // Before: config をfor 分で回して条件に一致したconfig だけkey

    で抽出してprops のkey に代入している for (propName in config) { if ( ... ) { if (enableRefAsProp && !disableStringRefs && propName === 'ref') { ... } else { props[propName] = config[propName]; // After: config を直接props に代入してReactElement に渡している let props; if (enableRefAsProp && disableStringRefs && !('key' in config)) { props = config; 19
  12. まとめ 今まではprops をクローンする必要があった 1. key, ref の予約語をprops から削除すること ref はprops

    で参照できるようになる、key もスプレット構文を使わなければ問題なし! 2. createElemet 後にprops の上書き React17 からjsx という関数が使われていて、それは我々開発者が使うことを推奨して いないようなので気にしないでヨシッ ! 実装 今までfor...in でぐるぐる回していたところを単純にfor 分で回さなくてよくなった 20