Unreleased

0.12.0

0.11.0

0.10.1

0.10.0

Breaking change

0.9.2

0.9.0 (05/2020)

Breaking Changes

Additions

Maintenance

Documentation

0.8.0 (04/2020)

v0.8.0 is a huge release that includes several breaking changes, maintenance fixes and documentation updates :tada:

Thanks to all of our contributors for making this happen!

Maintenance:

Breaking Changes:

Documentation:

0.7.1

Small release with a couple additional bindings to existing React APIs.

New APIs:

Also removes .cm* generated files from the release to clean up the build process.

0.7.0

Support for React Hooks API. Allows for writing function components with state and complex interactions.

You can now write components like

[@react.component]
let make = (~className, ~text) => <div className> {text} </div>;

which will compile to a 0-cost React component that looks like

let make = ({className, text}) => <div className> {text} </div>;

These components use a new PPX shipped with BuckleScript 5.0.0+. Please read the documentation for more.

There is no breaking change here, but if you're interested in migrating some components the upgrade script is provided. It will wrap existing ReasonReact components as if they are Hooks components.

0.6.0

Mini release before a surprise release next time (shhhhh) =).

Breaking

Improvements

0.5.0

This release requires bs-platform 4.0.3.

Small breaking change (only if you haven't upgraded for a while). The migration/upgrade script, as always, is here.

Event Changes

ReactEventRe is now deprecated in favor of ReactEvent. They're similar but the latter comes with the big quality-of-life improvement of turning our old:

ReactDOMRe.domElementToObj(ReactEventRe.Form.target(event))##value

into:

event->ReactEvent.Form.target##value

Aka, you can use the -> fast pipe now (|. in OCaml syntax), and we've changed the definition of target in the various ReactEvent modules to directly give you back a Js.t object instead of Dom.element. Same applies to other such attributes.

We've also changed things like ReactEventRe.Mouse._type into ReactEventRe.Mouse.type_ to abide by the Reason idiom.

Lastly, bs.send.pipe is informally deprecated, so we've removed the usage of those too. Instead of e |> ReactEventRe.Mouse.preventDefault, use either e->ReactEvent.Mouse.preventDefault or ReactEvent.Mouse.preventDefault(e). bs.send.pipe is, all things considered, the heaviest BuckleScript special annotation. If your library uses it, please consider removing it too. Thanks!

JSX

Fragment has landed! <> child1 child2 </>. For more info, check ReactJS' docs on Fragment. Note that we currently don't support:

The latter will be supported next. Fragment requires React 16.

Additionally, DOM component children spread <div>...foo</div> now works. No more need to use the ReasonReact.createDomElement fallback!

Removal of Previously Deprecated Features

Deprecations

Thanks for the wait, and enjoy!

0.4.2

This release requires bs-platform 3.1.4.

0.4.1

0.4.0

Requires bs-platform >=3.0.0. Migration script is here.

Breaking Changes

Deprecations

Improvements

0.3.4

This release requires bs-platform 2.2.2! If your app haven't upgraded to it, don't worry; you can still use ResonReact 0.3.2 just fine. Only two small changes.

0.3.2

0.3.1

No breaking change. The migration script is here.

Before: onClick={self.reduce(_event => Click)} After: onClick={_event => self.send(Click)}

Before: didMount: self => {self.reduce(() => Click, ()); NoUpdate} After: didMount: self => {self.send(Click); NoUpdate}

0.3.0

Technically a breaking change, but just because of the removal of a few deprecated things. No migration script this time; since the few breaking changes you encounter (if any) should have be spotted by the type system and fixed in a few secs anyway.

Improvements:

Breaking Changes:

0.2.4

Major update, but again with no breaking changes, and again with a convenience migration script! =)

The big change in this release is the deprecation of statefulComponent and statefulComponentWithRetainedProps. statelessComponent stays the same.

Prerequisites

Please first read the blog post.

After reading this migration guide, use the migration script (or not) like so: node node_modules/reason-react/migrate/from02xTo024.js myReasonFile.re.

Migrate From StatefulComponent to ReducerComponent

There's no more need for ReasonReact.statefulComponent. Every state change is now controlled through a dedicated, centralized, react-fiber-ready, component-local mechanism called "reducer" (aka, the hype word for "state machine").

Reason-react-example is updated too. Go check the examples afterward!

In short:

reducer: fun action state =>
  switch action {
  | Click => ReasonReact.Update {...state, foo: bar}
  }

We've also exposed new ReasonReact.SideEffects (aka ReasonReact.NoUpdate, with side-effect) and ReasonReact.UpdateWithSideEffects (ReasonReact.Update + side-effect).

The relevant section on actions, reducers and the new update additions are in the main docs.

If everything goes alright, we will be deprecating statefulComponent in the future

InstanceVars/React Ref Usage Changed

Before, we used to recommend using ReasonReact.SilentUpdate to deal with ReactJS' instance variables pattern (e.g. attaching properties onto the component class itself, like timer IDs, subscriptions, refs, etc.). Now we've moved to using a Reason ref cell (not the React ref, the mutative Reason ref). See the updated instance variables section.

The new recommendation also solves a corner-case bug with assigning more than one refs in the render.

LifeCycle: Future didMount and willReceiveProps Signature Change

The future ReactJS Fiber in ReasonReact won't work well with lifecycle events that return the new state, aka:

Please return ReasonReact.NoUpdate for the former (can't do much for the latter, willReceiveProps, for now. Keep it as it is). If you really need to trigger a state change, before the return, use a self.reduce (fun () => Bar) (), aka immediately apply a reduce.

We will make all lifecycles return unit in the future; it'll be an easy codemod to change ReasonReact.NoUpdate to nothing.

Miscellaneous Changes

Enjoy!

0.2.1

Breaking update (sorry!)

We've finally removed ReactRe. It's been deprecated since 0.1.4. And we've offered a comprehensive migration in the 0.1.4 section below.

We've given folks a bit of breathing room in terms of breaking changes; now we're shipping another one, this time with a small migration script. After installing reason-react, use node node_modules/reason-react/oldScriptCarefulMigrateFrom015To020.js myReasonFile.re

0.1.5

Non-breaking update. Works better with bs-platform >=1.8.0, which comes with the following ReasonReact JSX fixes:

Our own release contains the following improvements:

0.1.4

Major update! Though this is 100% backward compatible, so no major version bump. We've revamped the whole API based on all you awesome folks' feedback, and we've provided a gradual migration path.

Requirements & Self-Congratulations

Upon installing the new dependencies, your existing code still works. Isn't that great? You can incrementally convert things over. The old modules will stay around until the next or next next version. No rush!

ReactDOMRe, ReactDOMServerRe and ReactEventRe stay as-is. ReactRe is now deprecated (but again, is staying around) in favor of the new implementation, ReasonReact.

Small overview:

Forwarded Definitions

The following definitions are carried over from ReactRe into ReasonReact, unchanged. A simple search-and-replace fixes all of them:

JSX

Lowercase JSX <div> whatever </div> didn't change. Uppercase <Foo ref=a key=b bar=baz> hello goodbye </Foo> used to translate to Foo.createElement ref::a key::b bar::baz [hello, goodbye]. It now translates to ReasonReact.element ref::a key::b (Foo.make bar::baz [|hello, goodbye|]). We've pulled out ref and key into a dedicated call for good measures, and instead of using list as children, we now use array. More idiomatic ReactJS, list <-> array conversion churn.

To use the new JSX, change bsconfig.json's {"reason": {"react-jsx": true}} to {"reason": {"react-jsx": 2}}. Although you probably won't do that at the beginning, since that'd change all JSX in the whole project and cause type errors everywhere. Instead, keep your old bsconfig.json unchanged and for the JSX you'd like to selectively convert over, put a [@@@bs.config {jsx: 2}]; at the top of the file. Once you've converted everything over, switch to {"react-jsx": 2} in bsconfig.json and remove those @@@bs.config at the top of every file.

Alternatively, you can go straight to {"react-jsx": 2} in bsconfig.json, and put a [@@@bs.config {jsx: 1}] at the top of files where you'd like to use the old uppercase JSX transform.

Before starting the sections below, please briefly go through the new API on the documentation page.

Foo.createElement (Jargon Change)

Not to be confused with the ReactRe.createElement in the previous section.

Foo.createElement is now referred to as Foo.make. make is a more idiomatic term in Reason/OCaml, and is shorter to type!

componentBag

The concept of componentBag is now called self. We thought it'd be a more appropriate name. The new self doesn't contain props, state, setState and instanceVars anymore; these are no longer needed in the new ReasonReact.

componentBag.props

Replaced with the new make (previously createElement) call which takes in labeled arguments. See more in this section.

How to access props in the update/handle callbacks now? You'd move these callback definitions into the make function body.

componentBag.state

Now passed to you as an argument in callbacks and lifecycle events.

componentBag.instanceVars

No longer needed. In ReactJS, attaching instance variables onto a component has always been a sly way of introducing 1. mutative state that 2. doesn't trigger re-render. This whole concept is now replaced by putting your value into state and using ReasonReact.SilentUpdate (doesn't trigger re-render, but does update state) in callbacks & lifecycles.

componentBag.setState

Not to be confused with the ReactJS setState you often use (though if you're reading this migration guide, you probably know this already). This was an escape hatch designed for times when you can't, for example, return an option state from an updater, because you want to setState imperatively and asynchronously. The new idiom is to just use self.update myhandler (). Notice update myhandler returns a callback just like before, but now you're immediately applying the callback.

updater/handler

updater and handler are now called update and handle. They should be an easy search-and-replace.

The return type of update, instead of option state, is now update state.

The signature of the callback they take has changed. Instead of e.g. updater (fun {props, state, instanceVars} event => Some {...state, foo: true}), it's now update (fun event state self => ReasonReact.Update {...state, foo: true}). Formal, simplified type of the new callback: payload => state => self => update state.

update and handle don't memoize anymore; this reduces confusion and potential memory leaks.

Render

render, previously in your component module, is now inside make. See the example here.

Ref & Other Mutable Instance Variables

ref now lives inside state, as described in componentBag.instanceVars just above. Instead of componentBag.handler+ mutating instanceVars, you'd now use update and returning ReasonReact.SilentUpdate {...state, ref: theRef}. refs and others probably default to None in your initial state.

Lifecycles

We've decided to finally drop the component prefix to lifecycle methods! componentDidMount is now just called didMount, etc. The signatures changed slightly; see them in the new lifecycle events section.

Children

The children helpers & types reactJsChildren, listToElement and jsChildrenToReason are all gone from ReasonReact, since we now use array instead of list.

New Reason <-> JS Interop

wrapPropsShamelessly (Reason Calling JS)

It's not clear why we called it this way in the old API. Please tell @_chenglou that he should name things more seriously on a serious project. The new name is wrapJsForReason. The signature hasn't changed much; arguments are labeled now and explicitly accept an unlabeled children at the last position. Example:

Before:

external myJSReactClass : ReasonReact.reactClass = "myJSReactClass" [@@bs.module];

let createElement name::(name: string) age::(age: option int)=? =>
  ReactRe.wrapPropsShamelessly myJSReactClass {"name": name, "age": Js.Nullable.from_opt age};

After:

external myJSReactClass : ReasonReact.reactClass = "myJSReactClass" [@@bs.module];

let make name::(name: string) age::(age: option int)=? children =>
  ReasonReact.wrapJsForReason
    reactClass::myJSReactClass
    props::{"name": name, "age": Js.Nullable.from_opt age}
    children;

Don't forget that once these are converted over, the callsites of these components will need to use the new JSX transform described above. Otherwise they'll generate type errors.

jsPropsToReasonProps (JS Calling Reason)

Now called wrapReasonForJs. The file-level include that served this interop is gone; it used to magically export the backing component comp for JS consumption. You now have to manually export comp through the new wrapReasonForJs. Continuation of the previous example:

let component = ...;
let make ...;

let comp =
  ReasonReact.wrapReasonForJs
    ::component
    (fun jsProps => make name::jsProps##name age::?(Js.Nullable.to_opt jsProps##age) [||]);

The function takes in the labeled reason component, and a function that, given the js props, asks you to call make while passing in the correctly converted parameters.

Aaaand that's it! Enjoy!

0.1.3

DOM ref is now typed as Js.null Dom.element, instead of just Dom.element (https://github.com/reasonml/reason-react/commit/6f2a75b). Trivial migration: https://github.com/reasonml-community/reason-react-example/commit/b44587a

0.1.2

Add defaultValue prop for input DOM component (https://github.com/reasonml/reason-react/commit/c293c6e)

0.1.1

Correct type of dangerouslySetInnerHTML (#76)

0.1.0

Lots of great people working together.