Skip to content

Latest commit

 

History

History
252 lines (194 loc) · 5.46 KB

migrate-react.mdx

File metadata and controls

252 lines (194 loc) · 5.46 KB
titledescriptioncanonical
Migrate from JSX v3
Migrate from JSX v3
/docs/react/latest/migrate-react

Migrate from JSX v3

JSX v4 introduces a new idiomatic record-based representation of components which is incompatible with v3. Because of this, either the entire project or dependencies need to be compiled in v4 mode, or some compatibility features need to be used to mix v3 and v4 in the same project. This page describes how to migrate from v3 to v4.

Configuration

Remove the existing JSX configuration from rescript.json:

{ "reason": { "react-jsx": 3 } }

Then add the new JSX configuration:

{ "jsx": { "version": 4 } }

or, to keep using the legacy React.createElement API like with JSX v3:

{ "jsx": { "version": 4, "mode": "classic" } }

File-level config

The top-level attribute @@jsxConfig is used to update the jsx config for the rest of the file (or until the next config update). Only the values mentioned are updated, the others are left unchanged.

@@jsxConfig({ version: 4, mode: "automatic" }) moduleWrapper= { moduleR1= { @react.component// V4 and new _jsx transformletmake= () =>body } @@jsxConfig({ version: 4, mode: "classic" }) moduleR2= { @react.component// V4 with `React.createElement`letmake= () =>body } } @@jsxConfig({ version: 3 }) @react.component// V3letmake= () =>body

v3 compatible mode

JSX v3 is still available with the latest version of compiler and rescript-react.

{ "jsx": { "version": 3, "v3-dependencies": ["rescript-relay"] }, "bsc-flags": ["-open ReactV3"] }

To build certain dependencies in v3 compatibility mode, whatever the version used in the root project, use "v3-dependencies". The listed dependencies will be built-in v3 mode, and in addition -open ReactV3 is added to the compiler options.

Migration of v3 components

Some components in existing projects are written in a way that is dependent on the v3 internal representation. Here are a few examples of how to convert them to v4.

makeProps does not exist in v4

Rewrite this:

// V3moduleM= { @objexternalmakeProps: (~msg: 'msg, ~key: string=?, unit) => {"msg": 'msg} =""letmake= (~msg) => <div> {React.string(msg)} </div> }

To this:

// V4moduleM= { typeprops<'msg> = {msg: 'msg} letmake=props=> <div> {React.string(props.msg)} </div> }

React.Context

Rewrite this:

moduleContext= { letcontext=React.createContext(() => ()) moduleProvider= { letprovider=React.Context.provider(context) @react.componentletmake= (~value, ~children) => { React.createElement(provider, {"value": value, "children": children}) // Error } } }

To this:

moduleContext= { letcontext=React.createContext(() => ()) moduleProvider= { letmake=React.Context.provider(context) } }

React.forwardRef (Discouraged)

Rewrite this:

moduleFancyInput= { @react.componentletmake=React.forwardRef(( ~className=?, ~children, ref_, // argument ) => <div> <inputtype_="text" ?classNameref=?{ref_->Nullable.toOption->Option.map(ReactDOM.Ref.domRef)} /> children </div> ) } @react.componentletmake= (~onClick) => { letinput=React.useRef(Nullable.null) <div> <FancyInputref=input> // prop <buttononClick> {React.string("Click to focus")} </button> </FancyInput> </div> }

To this: In v3, there is an inconsistency between ref as prop and ref_ as argument. With JSX v4, ref is only allowed as an argument.

moduleFancyInput= { @react.componentletmake=React.forwardRef(( ~className=?, ~children, ref, // only `ref` is allowed ) => <div> <inputtype_="text" ?classNameref=?{ref->Nullable.toOption->Option.map(ReactDOM.Ref.domRef)} /> children </div> ) } @react.componentletmake= (~onClick) => { letinput=React.useRef(Nullable.null) <div> <FancyInputref=input> <buttononClick> {React.string("Click to focus")} </button> </FancyInput> </div> }

Mangling the prop name

The prop name was mangled automatically in v3, such as _open becomes open in the generated js code. This is no longer the case in v4 because the internal representation is changed to the record instead object. If you need to mangle the prop name, you can use the @as annotation.

Rewrite this:

moduleComp= { @react.componentletmake= (~_open, ~_type) => <Modal_open_type> <Description /> </Modal> }

To this:

moduleComp= { @react.componentletmake= (@as("open") ~_open, @as("type") ~_type) => <Modal_open_type> <Description /> </Modal> }

Bindings to JS components with optional props

Previously, you could wrap optional props with an explicit option when writing bindings to JS components. This approach functioned only due to an implementation detail of the ppx in JSX 3; it's not how to correctly write bindings to a function with optional arguments.

Rewrite this:

moduleButton= { @module("./Button") @react.componentexternalmake: (~text: option<string>=?) =>React.element="default" }

To this:

moduleButton= { @module("./Button") @react.componentexternalmake: (~text: string=?) =>React.element="default" }
close