- Remove legacy
ReactDOMRe
andReasonReact
modules (@anmonteiro in #782) - Fix
React.useCallback*
for callbacks with multiple arguments (@anmonteiro in #786) - Wrap the
React
library, exposing just a single top-level module (@anmonteiro in #783)
- Migrate the reason-react PPX and library to the new React JSX transform (@anmonteiro in #714)
- Add
suppressHydrationWarning
to supported props (@davesnx in #721) - Rename
reactjs-jsx-ppx
toreason-react-ppx
(@davesnx in #732) - Fix locations for lower and uppercase components so that merlin / editor integration can get type defs on hover (@jchavarri in #748)
- Refine types for
key
attributes (@anmonteiro in #750) - Bump React depext to v17-18 #777 (@jchavarri in #777)
- Make optional props optional properly (@davesnx in #776)
- Setup repo with Melange @davesnx in #711
- Add dune file @davesnx in #709
- Upgrade bs-platform to 9.0.2 @davesnx in #700
- Update Drag.dataTransfer to Js.t @davesnx in #701
- Added as property to DOM props @cornedor in #603
- Stops versioning
lib/js
@bloodyowl in #552 - Use simpler constructs for createRoot @bloodyowl in #546
- Adds
ReactErrorBoundary
@bloodyowl in 577 - Adds React StrictMode @relekang in #572
- Adds tests @bloodyowl in #575
- Adds
[@bs.uncurry]
touseReducerWithMapState
@johnridesabike in #570 - Moves reason-test-framework to
devDependency
@bloodyowl in #578 - Adds React.component as a new binding for making components @rickyvetter in #571
- Uses console.error for hydrate function @bloodyowl in #545
- Adds pointer events @nickretallack in #584
- ReactDOM simplification @bloodyowl in #547
- Adds uncurried hooks @johnridesabike in #551
- Adds
docs
to.npmignore
@peterpme in #583
- Fixed typo in blog post @maxdeviant in #569
- Importing components with default props @johnridesabike in #573
- Adds binding to external context docs @alavkx in #495
- Adds Personas by Draftbit example @dylanirlbeck in #586
v0.8.0 is a huge release that includes several breaking changes, maintenance fixes and documentation updates 🎉
Thanks to all of our contributors for making this happen!
- Moves
bs-platform
topeerDependencies
- Moves
react
,react-dom
topeerDependencies
@sync in #361 - Removes compilation warnings @bloodyowl in #539
bs-platform
has a minimum version of 7.1.1 (^7.1.1) @imbsky in #503- Adds
onInvalid
prop @bsansouci in #364 - Adds
React.float
andReact.int
@utkarshkukreti in #420 - Fix
crossOrigin
case and type @schmavery in #469 - Adds
mapWithIndex
andforEachWithIndex
tochildren
@cem2ran in #515 - Fixes
cloneElement
type definition @bloodyowl in #464 - Removes
maxDuration
prop from suspense @anmonteiro in #399 - Adds
React.SuspenseList
@ri7nz in #533 - Simplifies ref type @cknitt in #521
- Uses
console.error
instead of throwing @bloodywl in #520 - Adds
unsafeAddStyle
andcombine
@sgny in #437 - Uses an external for
ReactDOMRE.Style.combine
and removesObj.magic
@bloodyowl in #519 - Adds
autoCapitalize
@wegry in #304 - Adds bindings for
useTransition
hook @kiangtengl in #512 - Sets
min
attribute tostring
@stuartkeith in #281 - Adds
displayName
@czystyl in #433 - Adds Experimental module @anmonteiro in #400
- Adds Apollo / GraphQL Recipe @dylanirlbeck in #537
- Updates docs to be more user friendly @peterpme in #534
- Fix typo in render props @vdanchenkov in #535
- Update Examples based off React.js Docs & organize a bit @peterpme in #532
- Binding to external context @alavkx in #495
- Updates component-as-prop docs @vonwao in #463
- Improves
useEffect
docs @sgny in #461 - Clarifies
useEffect
docs @pckilgore in #336 - Adds notes about using
children
with Hooks API @anmonteiro in #396 - Adds repl.it link @amasad in #244
Small release with a couple additional bindings to existing React APIs.
New APIs:
React.cloneElement
React.Children
useEffect
,useLayoutEffect
,useMemo
,useCallback
,useImperativeHandle
now are all consistently implemented up touseX7
to improve usability.ReasonReactRouter.urlNotEqual
Also removes .cm*
generated files from the release to clean up the build process.
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.
Mini release before a surprise release next time (shhhhh) =).
- The DOM prop
crossorigin
is now correctly namedcrossOrigin
. This is a tiny breaking change, thus the version bump (#290).
- Fix issue with react-hot-loader (#298).
ReasonReact.Router.replace
to replace state.
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.
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!
Fragment has landed! <> child1 child2 </>
. For more info, check ReactJS' docs on Fragment. Note that we currently don't support:
- Keyed fragment.
- Fragment with ref.
- Fragment spread (ReasonReact-specific):
<> ...children </>
.
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!
- The DOM props
_type
,_open
, and others (see 0.4.2 release notes below) are officially removed. Usetype_
,open_
, etc. - The field
subscriptions
was deprecated and is now removed completely. Please use the new subscriptions helper instead. ReasonReact.stringToElement
,nullElement
,arrayToElement
are also gone for good. UseReasonReact.string
,null
,array
. The previous migration script in0.4.0
already took care of this.ReasonReact.Callback
module removed.
ReasonReact.createDomElement
is changed in favor ofReactDOMRe.createElementVariadic
. This is more consistent withReactDOMRe.createElement
. Both are used by the JSX transform; the latter, when it's a children spread for DOM elements (mentioned above) and has a small perf cost.
Thanks for the wait, and enjoy!
This release requires bs-platform 3.1.4
.
- DOM components now support
aria-*
attributes without needing hacks:<div ariaLabel="foo" />
. The camelCaseariaStuff
will compile toaria-stuff
. - For DOM props, instead of
_open
,_type
,_begin
,_end
,_in
,_to
, use the new trailing underscore version for consistency:open_
,type_
, etc. The former leading underscore versions are now deprecated.
- Restore
ReasonReact.Callback
module for now and put a deprecation warning on it. This way, at least your third-party dependencies can compile further. - Relax
wrapJsForReason
andwrapReasonForJs
types so that we can usebs.abstract
for interop instead ofJs.t
objects.
Requires bs-platform >=3.0.0
. Migration script is here.
- Remove
SilentUpdate
andSilentUpdateWithSideEffects
. These aren't used for a long time now. didMount
now returnsunit
, per our previous warnings in the docs. To trigger a state update, useself.send(MyAction)
.- Remove
self.reduce
for real; useself.send
(not to be confused withreducer
, which is still around). - Remove
ReasonReact.Callback
module (undocumented and unused).
ReasonReact.stringToElement
,arrayToElement
andnullElement
are now deprecated in favor ofReasonReact.string
,array
andnull
!- Old
subscriptions
API is deprecated. Please use the newself.onUnmount
. willReceiveProps
deprecated. We'll transition to React 16 very soon and release our binding toderiveStateFromProps
.
- Use
bool
instead ofJs.boolean
(thanks to BuckleScript 3.0.0). - Fix Router incompatibility with IE11 (#201).
- Much more streamlined internals.
- Prepare to transition to React 16.
- Did we mention that
ReasonReact.stringToElement
is nowReasonReact.string
, etc.?
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.
- Use the new bs-platform
Js.Nullable.toOption
. No more deprecation warnings when you use ReasonReact (main purpose of the release). - Add
ReactDOMRe.hydrate
,hydrateToElementWithId
,hydrateToElementWithClassName
(#184).
- Portal support (#152)
- Add
Router.dangerouslyGetInitialUrl
. Please see the corresponding docs on router.
No breaking change. The migration script is here.
- New subscriptions helper.
- Router is here!
self.reduce
is now changed intoself.send
, with a simpler semantic that clears up the confusion on the immediate call case. More performant and fewer allocations too! The migration script will convert most of the code over for you.
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}
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:
- Loosen
children
's restriction. This unlocks huge potentials. See the blog post! - Fix a bug where side effects inside side effects were skipped (#98).
- React 16 support.
- All files upper-cased. This follows the new community idiom. Technically an internal change; doesn't affect your usage of ReasonReact.
- More DOM props. There are like, more DOM props added per year than the number of releases we have. What a funny time to be alive.
Breaking Changes:
ref
is now typed asJs.nullable(Dom.element)
, notJs.null(Dom.element)
.Js.nullable
is the new community idiom from BuckleScript. Go through your codebase and change yourstate.myRef := Js.Null.to_opt(theRef)
intostate.myRef := Js.Nullable.to_opt(theRef)
. We suggest you to also remove all mentions ofJs.null
,Js.undefined
,Js.Null
andJs.Undefined
.Js.Nullable
checks for both JavaScriptnull
andundefined
, thus making the JS interop more robust. In the past, people type certain values (especially js objects fields) as e.g.null
and forget they can beundefined
.- DOM components (
ReactDOMRe
)'sopen
,end
andin
attributes are now changed to_open
,_end
and_in
to avoid using reserved keywords. This is only breaking if you're using these attributes and are on the old syntax. - Remove old stuff:
enqueue
,statefulComponent
andself.update
. These have long been deprecated.
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.
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
.
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:
- Replace all
ReasonReact.statefulComponent
withReasonReact.reducerComponent
. - Replace e.g.
self.update handleClick
(wherehandleClick
isfun self => ReasonReact.Update {...self.state, foo: bar}
) withself.reduce (fun _ => Click)
.Click
is just a variant constructor you've defined. Let's call it "action". - Add a
reducer
function to the body of your...component
spread:
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
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.
The future ReactJS Fiber in ReasonReact won't work well with lifecycle events that return the new state, aka:
didMount
'sReasonReact.Update {...state, foo: bar}
willReceiveProps
state
.
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.
- Add
defaultChecked
,loop
and others to DOM attribute (#29, #37, #44, #50). - Fix
cloneElement
binding (#49). - Fix stateless components's
willReceiveProps
's return value. It's nowunit
again. - Fix wrong version of
retainedProps
inwillReceiveProps
. - Remove the dependency on
create-react-class
. Now we're back to being dependency-free! - Bump react/react-dom to 16.
- React/react-dom are now dependencies, rather than peerDependencies. This follows the Reason/BS idiom of making the bound library an implementation detail. NPM/Yarn will still dedupe multiple versions of react/react-dom correctly; no worries about that.
Enjoy!
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
- Instead of
fun state self => ...
, we've now rolledstate
intoself
, and now, you havefun {state, handle} => ...
. The whole record isself
. Feel free to destructure and get whatever you need! self
now contains a new prop,retainedProps
. This is a new (non-breaking) feature that solves the previous slightly inconvenient way of forwarding props to state, as described in the old API's lifecycle methods. Now there's a dedicated API for it! The docs describes this in detail.
Non-breaking update. Works better with bs-platform >=1.8.0, which comes with the following ReasonReact JSX fixes:
- JSX ppx now recursively transforms component's props.
- JSX ppx now reports the correct location for some errors.
- JSX ppx now correctly transforms some corner case with ref and key (
ref=?foo
).
Our own release contains the following improvements:
- Adjust ReactDOMRe's
props
andstyle
to include more accurate DOM and style attributes and styles (#9, #15, #17). - Add
ReactDOMRe.Style.unsafeAddProp
to unsafely add a prop to an existingstyle
. Make sure you know what you're doing! - Fix
reactRef
's type in various locations. A React ref is actually always nullable; we've previously only acknowledged it for DOM ref, now we do for custom (composite) components ref too. A more detailed explanation is here. This is documented in our docs in the ref section as well. - Add
cloneElement
(solves adding otherwise invalid keys like aria-label and data-foo). - Add
shouldUpdate
.
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.
- New BuckleScript. bs-platform >=1.7.5
- This repo. reason-react >=0.1.4
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:
- No more modules/functors needed for the API (don't go crazy with the new function composition power please!)
- No more
include
- 10-20% smaller code on average, both input and output
- Corner-cases with
children
and empty props mostly gone - Unused props now warn
The following definitions are carried over from ReactRe
into ReasonReact
, unchanged. A simple search-and-replace fixes all of them:
ReactRe.reactElement
->ReasonReact.reactElement
- like wise,
reactClass
reactRef
,refToJsObj
nullElement
,stringToElement
,arrayToElement
createElement
(if you recall, this isn't the pervasive ReactJSReact.createElement
). It's only used raw in escape hatch situations. If you've never used it: great!
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.
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!
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.
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.
Now passed to you as an argument in callbacks and lifecycle events.
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.
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
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
, previously in your component module, is now inside make
. See the example here.
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}
. ref
s and others probably default to None
in your initial state.
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.
The children helpers & types reactJsChildren
, listToElement
and jsChildrenToReason
are all gone from ReasonReact
, since we now use array instead of list.
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.
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!
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
Add defaultValue
prop for input DOM component (https://github.com/reasonml/reason-react/commit/c293c6e)
Correct type of dangerouslySetInnerHTML
(#76)
Lots of great people working together.