reason-react
Advanced tools
Comparing version 0.3.0 to 0.3.1
--- | ||
id: callback-handlers | ||
title: Callback Handlers | ||
@@ -21,3 +20,3 @@ --- | ||
let make (~name, ~onClick, _children) => { | ||
let make = (~name, ~onClick, _children) => { | ||
...component, | ||
@@ -28,3 +27,3 @@ render: (self) => <button onClick=onClick /> | ||
No surprise here. Since Reason's JSX has [punning syntax](https://reasonml.github.io/guide/language/jsx#punning), that `button` will format into `<button onClick />`. | ||
No surprise here. Since Reason's JSX has [punning syntax](https://reasonml.github.io/docs/en/jsx.html), that `button` will format into `<button onClick />`. | ||
@@ -36,4 +35,4 @@ Similarly, to pre-process a value before sending it back to the component's owner: | ||
let make (~name, ~onClick, _children) => { | ||
let click event => onClick name; /* pass the name string up to the owner */ | ||
let make = (~name, ~onClick, _children) => { | ||
let click = (event) => onClick(name); /* pass the name string up to the owner */ | ||
{ | ||
@@ -52,3 +51,3 @@ ...component, | ||
let component = ...; | ||
let make (~name, ~onClick, _children) => | ||
let make = (~name, ~onClick, _children) => { | ||
let click = (event, self) => { | ||
@@ -72,3 +71,3 @@ onClick(event); | ||
**Note 2**: sometimes you might be forwarding `handle` to some helper functions. Pass the whole `self` instead and **annotate it**. This avoids a complex `self` record type behavior. See [Record Field `reduce`/`handle` Not Found](record-field-reduce-handle-not-found.md). | ||
**Note 2**: sometimes you might be forwarding `handle` to some helper functions. Pass the whole `self` instead and **annotate it**. This avoids a complex `self` record type behavior. See [Record Field `send`/`handle` Not Found](record-field-send-handle-not-found.md). | ||
@@ -95,4 +94,4 @@ #### Explanation | ||
``` | ||
let handleSubmitEscapeHatch = (username, password, event) => | ||
```reason | ||
let handleSubmitEscapeHatch = (username, password, event) => | ||
self.handle((tupleOfThreeItems) => doSomething(tupleOfThreeItems))(username, password, event)); | ||
@@ -112,2 +111,2 @@ ... | ||
You can't update state in `self.handle`; you need to use `self.reduce` instead. See the next section. | ||
You can't update state in `self.handle`; you need to use `self.send` instead. See the next section. |
--- | ||
id: children | ||
title: Children | ||
@@ -8,5 +7,8 @@ --- | ||
For example, in ReactJS, you'd often want to constrain the `children` being passed to you as a single child. So inside your own render, you'd do: | ||
Let's say people are using your component like so: `<Animation><div /></Animation>`. In the `Animation` component, you'd want to constrain the `children` being passed to you (the `div` here) as a single child. Aka, you'd like to error on this: `<Animation><div /><div /></Animation>`. | ||
ReactJS provides such helper: | ||
```js | ||
// inside Animation.js | ||
render: () => { | ||
@@ -17,6 +19,10 @@ return React.Children.only(this.children); | ||
Or, maybe you accept two (and exactly two) children: | ||
Or, maybe you made a Layout component and accepts exactly two children (e.g. `<Layout><MyColumn /><MyColumn /></Layout>`): | ||
```js | ||
// Layout.js | ||
render: () => { | ||
if (React.Children.count(this.props.children) !== 2) { | ||
// ... error | ||
} | ||
return ( | ||
@@ -32,5 +38,6 @@ <div> | ||
Or maybe you're actually rendering a children callback: | ||
Or maybe you mandate a children callback: | ||
```js | ||
render: () => { | ||
@@ -41,23 +48,72 @@ return React.Children.only(this.props.children('hello')); | ||
And for more complicated cases, you'd start using some elaborate `React.Children.*` utils. None of these are type-safe (naturally, since JS is dynamic): | ||
As an author of these JS components, you can only hope that the user provided the right children type to you, and throw an error for them if not. In ReasonReact, `children` prop is typed and prevents bad usage from the consumer. This seems pretty natural, once you realize that `children` props **is basically like any other prop**! Yes, you can pass tuple, variant, record and others to children! This also mean we don't have to provide the equivalent `React.Children.*` utils in ReasonReact at all! | ||
```js | ||
<MyForm> <div /> <div /> </MyForm> | ||
// oops, this blows up because `MyForm` expects a single child | ||
However, due to syntactical and interoperability reasons, we do have some small restrictions on `children`: | ||
- DOM elements such as `div`, `span` and others mandate the `children` you pass to be `array(reactElement)`. For example, `<div> [1, 2, 3] </div>` or `<span> Some(10) </span>` don't work, since the `list` and the `option` are forwarded to the underlying ReactJS `div`/`span` and such data types don't make sense in JS. | ||
- User-defined elements, by default, also accept an array (of anything). This is due to a rather silly constraint of the JSX syntax, described below. | ||
## Syntax Constraint | ||
_Technically_, if you've written a component that accepts a tuple as `children`: | ||
```reason | ||
/* MyForm.re */ | ||
type tuple2Children = (ReasonReact.reactElement, ReasonReact.reactElement); | ||
let make = (children: tuple2Children) => { | ||
...component, | ||
render: _self => { | ||
<div> | ||
{fst(children)} | ||
<Separator /> | ||
{snd(children)} | ||
</div> | ||
} | ||
}; | ||
``` | ||
We can do better in ReasonReact through simply using the language! | ||
Then you can expect the user you pass you a tuple: | ||
```reason | ||
<MyForm> ...(<div />, <div />) </MyForm> | ||
<MyForm> (<div />, <div />) </MyForm> | ||
``` | ||
Two things: | ||
This, however, will give you a type error: | ||
- `MyForm`'s children is expected to be a tuple of 2 react elements. | ||
``` | ||
This has type: | ||
array('a) | ||
But somewhere wanted: | ||
tuple2Children (defined as (ReasonReact.reactElement, ReasonReact.reactElement)) | ||
``` | ||
- The presence of `...` is called [children spread](https://reasonml.github.io/guide/language/jsx#children-spread). Passing `<MyForm> (<div />, <div />) </MyForm>` would wrap the tuple in an array (see the [JSX](jsx.md#children) section). | ||
It means that `MyForm` is expecting the tuple, but you're giving `array` instead! What's happening? Well, look at what JSX is transformed into: | ||
Here are some use-cases for children + children spread + Reason built-in data structures: | ||
```reason | ||
<MyLayout> a b </MyLayout> | ||
<MyLayout> a </MyLayout> | ||
``` | ||
These actually become: | ||
```reason | ||
ReasonReact.element( | ||
MyLayout.make([|a, b|]) | ||
); | ||
ReasonReact.element( | ||
MyLayout.make([|a|]) | ||
); | ||
``` | ||
See how the second `MyLayout`'s children is also wrapped in an array? We can't special-case `<MyLayout> a </MyLayout>` to pass `a` without the array wrapping (it'd give even more confusing errors). Since children is usually an array, we've decided to always wrap it with an array, no matter how many items we visually see in the JSX. But what if you really want to pass unwrapped data? | ||
### Children Spread | ||
Just use `<MyLayout> ...a </MyLayout>`. This will simply transform into `ReasonReact.element(MyLayout.make(a))`, aka without the array wrapping. | ||
#### Tips & Tricks | ||
Here are some use-cases for children spread + Reason built-in data structures: | ||
- A layout component that mandates a tuple of 2 react element and shows them side-by-side: | ||
@@ -81,6 +137,2 @@ | ||
How do we know that `MyForm`, `Motion` and `Layout` accept such children? Well, that'll simply be inferred by internal usage of `children` in these components' respective `render`. Just another day in the magical land of type inference. | ||
Go wild! | ||
### Pitfall | ||
@@ -87,0 +139,0 @@ |
--- | ||
id: clone-element | ||
title: cloneElement | ||
@@ -24,3 +23,3 @@ --- | ||
This will assign the extra `data-payload` and `aria-label` props (**both untyped**, be careful!) onto the `div`, through a clever, syntactically valid use of Reason's JS object sugar for [BuckleScript objects](http://bucklescript.github.io/bucklescript/Manual.html#_create_js_objects_using_bs_obj). | ||
This will assign the extra `data-payload` and `aria-label` props (**both untyped**, be careful!) onto the `div`, through a clever, syntactically valid use of Reason's JS object sugar for [BuckleScript objects](https://bucklescript.github.io/docs/en/object.html#object-as-record). | ||
@@ -27,0 +26,0 @@ For non-DOM components, you need to use valid prop names. |
--- | ||
id: community | ||
title: Community | ||
--- | ||
This is where we keep track of all the awesome things happening in the community. | ||
@@ -10,9 +10,9 @@ | ||
- [Discord](https://discord.gg/reasonml) | ||
- [Twitter](https://twitter.com/reasonml) | ||
- [Reddit](https://www.reddit.com/r/reasonml/) | ||
- [Stack Overflow](http://stackoverflow.com/questions/tagged/reason-react) | ||
- IRC (freenode #reasonml) | ||
### Github | ||
- [ReasonReact repo](https://github.com/reasonml/reason-react) |
--- | ||
id: component-as-prop | ||
title: Component as Prop | ||
--- | ||
In ReactJS, `<Menu banner=MyBanner />` is easy; in ReasonReact, we can't trivially pass the whole component module ([explanations](https://reasonml.github.io/guide/language/module)). Solution: | ||
In ReactJS, `<Menu banner=MyBanner />` is easy; in ReasonReact, we can't trivially pass the whole component module (it wouldn't even be syntactically valid. A module resides in another layer of language. [Explanations](https://reasonml.github.io/docs/en/module.html)). Solution: | ||
@@ -13,1 +12,6 @@ ```reason | ||
``` | ||
This also has some added advantages: | ||
- The _owner_ is the one controlling how the component renders, not some opaque logic inside `MyBanner`. | ||
- Circumvents the tendency of needing a [props spread](props-spread.md) in `MyBanner`'s render, since passing a component to a child and the child using prop spread on said component usually go together. |
--- | ||
id: counter | ||
title: Counter | ||
--- | ||
Demonstrates calling an async "setState" in idiomatic ReasonReact | ||
Demonstrates calling an async "setState" in ReasonReact. | ||
```reason | ||
type action = | ||
| Tick; | ||
type state = { | ||
@@ -16,15 +18,51 @@ count: int, | ||
let make = (_children) => { | ||
let make = _children => { | ||
...component, | ||
initialState: () => {count: 0, timerId: ref(None)}, | ||
reducer: (action, state) => | ||
switch action { | ||
switch (action) { | ||
| Tick => ReasonReact.Update({...state, count: state.count + 1}) | ||
}, | ||
didMount: (self) => { | ||
self.state.timerId := Some(Js.Global.setInterval(self.reduce((_) => Tick), 1000)); | ||
ReasonReact.NoUpdate | ||
didMount: self => { | ||
self.state.timerId := | ||
Some(Js.Global.setInterval(() => self.send(Tick), 1000)); | ||
ReasonReact.NoUpdate; | ||
}, | ||
render: ({state}) => <div> (ReasonReact.stringToElement(string_of_int(state.count))) </div> | ||
willUnmount: self => { | ||
switch (self.timerId^) { | ||
| Some(id) => Js.Global.clearInterval(id) | ||
| None => () | ||
} | ||
}, | ||
render: ({state}) => | ||
<div>{ReasonReact.stringToElement(string_of_int(state.count))}</div> | ||
}; | ||
``` | ||
Or, using the [subscriptions helper](subscriptions-helper.md): | ||
```reason | ||
type action = | ||
| Tick; | ||
type state = {count: int}; | ||
let component = ReasonReact.reducerComponent("Counter"); | ||
let make = _children => { | ||
...component, | ||
initialState: () => {count: 0}, | ||
reducer: (action, state) => | ||
switch (action) { | ||
| Tick => ReasonReact.Update({...state, count: state.count + 1}) | ||
}, | ||
subscriptions: self => [ | ||
Sub( | ||
() => Js.Global.setInterval(() => self.send(Tick), 1000), | ||
Js.Global.clearInterval | ||
) | ||
], | ||
render: ({state}) => | ||
<div>{ReasonReact.stringToElement(string_of_int(state.count))}</div> | ||
}; | ||
``` |
--- | ||
id: creation-props-self | ||
title: Creation, Props & Self | ||
@@ -88,7 +87,7 @@ --- | ||
It says "I understand that `age` is optional and that when I use the label I should pass an int. But I'd like to forward an `option` value explicitly". This isn't a JSX trick we've made up; it's just part of the language feature! See the section on "Explicitly Passed Optional" in the [Reason docs](https://reasonml.github.io/guide/language/function/#explicitly-passed-optional). | ||
It says "I understand that `age` is optional and that when I use the label I should pass an int. But I'd like to forward an `option` value explicitly". This isn't a JSX trick we've made up; it's just a language feature! See the section on "Explicitly Passed Optional" in the [Reason docs](https://reasonml.github.io/docs/en/function.html#explicitly-passed-optional). | ||
## `self` | ||
You might have seen the `render: (self) => ...` part in `make`. The concept of JavaScript `this` doesn't exist in ReasonReact (but can exist in Reason, since it has an optional object system); the `this` equivalent is called `self`. It's a record that contains `state`, `retainedProps`, `handle` and `reduce`, which we pass around to the lifecycle events, `make` and a few others, when they need the bag of information. These concepts will be explained later on. | ||
You might have seen the `render: (self) => ...` part in `make`. The concept of JavaScript `this` doesn't exist in ReasonReact (but can exist in Reason, since it has an optional object system); the `this` equivalent is called `self`. It's a record that contains `state`, `retainedProps`, `handle` and `reduce`, which we pass around to the lifecycle events, `render` and a few others, when they need the bag of information. These concepts will be explained later on. | ||
--- | ||
id: dom | ||
title: Working with DOM | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: example-projects | ||
title: Example Projects | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: functional-component | ||
title: Functional Component | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: i-really-need-feature-x-from-reactjs | ||
title: I Really Need Feature X From ReactJS | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: im-having-a-type-error | ||
title: I'm Having a Type Error | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: installation | ||
title: Installation | ||
--- | ||
**Note**: for general Reason + BuckleScript editor setup, see [here](https://reasonml.github.io/docs/en/global-installation.html). | ||
To easily try ReasonReact, we offer two solutions with different goals in mind. | ||
@@ -10,20 +11,20 @@ | ||
**Goals**: simplicity, control, traditional app with several html files. | ||
Our preferred option in most cases. If it's your first time trying ReasonReact, feel free to use the more familiar create-react-app option below, too. | ||
**Prerequisites**: having `bsb` installed, through `npm install -g https://github.com/BuckleScript/bucklescript`\*. Further installation instructions [here](http://bucklescript.github.io/bucklescript/Manual.html#_installation). | ||
```sh | ||
npm install -g bs-platform | ||
bsb -init my-react-app -theme react | ||
cd my-react-app && npm install && npm start | ||
# in another tab | ||
npm run webpack | ||
``` | ||
\* **Note** that this installs BuckleScript directly from source. Installing `bs-platform` from npm gives you a version whose `react` template has a few bugs currently. This will be fixed soon. | ||
BuckleScript's [bsb](https://bucklescript.github.io/docs/en/build-overview.html) build system has an `init` command that generates a project template. The `react` theme offers a lightweight solution optimized for low learning overhead and ease of integration into an existing project. | ||
`bsb -init my-react-app -theme react` | ||
BuckleScript's [bsb](http://bucklescript.github.io/bucklescript/Manual.html#_bucklescript_build_system_code_bsb_code) build system has an `init` command that generates a project template. The `react` theme offers a lightweight solution optimized for low learning overhead and ease of integration into an existing project. | ||
It compiles to straighfoward JS files, so you can open `index.html` directly from the file system. No server needed. | ||
## Reason Scripts (with Development Server) | ||
## Reason Scripts (Aka Create-React-App) | ||
**Goals**: single-page app (SPA), builds on best practices from create-react-app community. | ||
[Reason-scripts](https://github.com/reasonml-community/reason-scripts) provides a familiar experience to the ReactJS users who are already familiar with [create-react-app](https://github.com/facebookincubator/create-react-app). It's an all-encompassing solution. However, if it's too heavy for your taste, try the first option above (bsb). | ||
As with `create-react-app`, `reason-scripts` comes with a server and hotloading built in. |
--- | ||
id: instance-variables | ||
title: Instance Variables | ||
@@ -16,3 +15,3 @@ --- | ||
In reality, this is nothing but a thinly veiled way to mutate a component's "state", without triggering a re-render. ReasonReact asks you to correctly put these instance variables into your component's `state`, into Reason [`ref`s](https://reasonml.github.io/guide/language/mutation). | ||
In reality, this is nothing but a thinly veiled way to mutate a component's "state", without triggering a re-render. ReasonReact asks you to correctly put these instance variables into your component's `state`, into Reason [`ref`s](https://reasonml.github.io/docs/en/mutation.html). | ||
@@ -19,0 +18,0 @@ ```reason |
--- | ||
id: interop | ||
title: Interop With Existing ReactJS Components | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: intro-example | ||
title: Intro Example | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: invalid-prop-name | ||
title: Invalid Prop Name | ||
--- | ||
Prop names like `type` (as in `<input type="text" />`) aren't syntactically valid; `type` is a reserved keyword in Reason/OCaml. Use `<input _type="text" />` instead. This follows BuckleScript's [name mangling rules](http://bucklescript.github.io/bucklescript/Manual.html#_object_label_translation_convention). | ||
Prop names like `type` (as in `<input type="text" />`) aren't syntactically valid; `type` is a reserved keyword in Reason/OCaml. Use `<input _type="text" />` instead. This follows BuckleScript's [name mangling rules](https://bucklescript.github.io/docs/en/object.html#invalid-field-names). | ||
@@ -8,0 +7,0 @@ For `data-*` and `aria-*`, this is a bit trickier; words with `-` in them aren't valid in Reason/OCaml. When you do want to write them, e.g. `<div aria-label="click me" />`, use [`cloneElement`](clone-element.md) as a workaround. |
--- | ||
id: js-using-reason | ||
title: ReactJS using ReasonReact | ||
--- | ||
PageReason.re: | ||
`PageReason.re`: | ||
```reason | ||
@@ -13,7 +14,7 @@ let component = ReasonReact.statelessComponent("PageReason"); | ||
let greeting = | ||
switch extraGreeting { | ||
switch (extraGreeting) { | ||
| None => "How are you?" | ||
| Some((g)) => g | ||
| Some(g) => g | ||
}; | ||
<div> <MyBannerRe show=true message=(message ++ (" " ++ greeting)) /> </div> | ||
<div> <MyBannerRe show=true message={message ++ " " ++ greeting} /> </div> | ||
} | ||
@@ -24,3 +25,3 @@ }; | ||
ReasonReact.wrapReasonForJs( | ||
~component=component, | ||
~component, | ||
(jsProps) => | ||
@@ -33,7 +34,8 @@ make( | ||
); | ||
``` | ||
``` | ||
Then use it on the JS side through | ||
```javascript | ||
var PageReason = require('path/to/PageReason.js').jsComponent; | ||
``` |
--- | ||
id: jsx | ||
title: JSX | ||
--- | ||
Reason comes with the [JSX](https://reasonml.github.io/guide/language/jsx) syntax! ReasonReact transforms it from an agnostic function call into a ReasonReact-specific call through a macro. To take advantage of ReasonReact JSX, put `{"reason": {"react-jsx": 2}` in your [`bsconfig.json`](http://bucklescript.github.io/bucklescript/Manual.html#_bucklescript_build_system_code_bsb_code) (schema [here](http://bucklescript.github.io/bucklescript/docson/#build-schema.json)). | ||
Reason comes with the [JSX](https://reasonml.github.io/docs/en/jsx.html) syntax! ReasonReact transforms it from an agnostic function call into a ReasonReact-specific call through a macro. To take advantage of ReasonReact JSX, put `{"reason": {"react-jsx": 2}` in your [`bsconfig.json`](https://bucklescript.github.io/docs/en/build-configuration.html#reason-refmt) (schema [here](http://bucklescript.github.io/bucklescript/docson/#build-schema.json)). | ||
**Note that due to current syntax constraints, you need to put spaces around the JSX children**: `<div> foo </div>`. | ||
### Uncapitalized | ||
@@ -73,10 +70,10 @@ | ||
```reason | ||
<MyForm> <div /> <div /> </MyForm> | ||
<MyReasonComponent> <div /> <div /> </MyReasonComponent> | ||
``` | ||
You're effectively passing the array `[| <div />, <div /> |]` to `MyForm`'s children. But this also means that the following wouldn't work: | ||
You're effectively passing the array `[| <div />, <div /> |]` to `MyReasonComponent`'s children. But this also means that the following wouldn't work: | ||
```reason | ||
let theChildren = [| <div />, <div /> |]; | ||
<MyForm> theChildren </MyForm> | ||
<MyReasonComponent> theChildren </MyReasonComponent> | ||
``` | ||
@@ -93,7 +90,7 @@ | ||
Which wraps the already wrapped `theChildren` in another layer of array. To solve this issue, Reason has a special [children spread syntax](https://reasonml.github.io/guide/language/jsx#children-spread): | ||
Which wraps the already wrapped `theChildren` in another layer of array. To solve this issue, Reason has a special [children spread syntax](https://reasonml.github.io/docs/en/jsx.html#children-spread): | ||
```reason | ||
let theChildren = [| <div />, <div /> |]; | ||
<MyForm> ...theChildren </MyForm> | ||
<MyReasonComponent> ...theChildren </MyReasonComponent> | ||
``` | ||
@@ -100,0 +97,0 @@ |
--- | ||
id: props-spread | ||
title: Props Spread | ||
--- | ||
You can't currently. Props spreading is a big source of unpredictability and performance regression (think `shouldComponentUpdate`). Our API prevents this. If you reaaaaally need it for binding to existing ReactJS components, see [this section](clone-element.md). | ||
You can't. Props spreading is a big source of unpredictability and performance regression (think `shouldComponentUpdate`). Our API prevents this. If you reaaaaally need it for binding to existing ReactJS components, see [this section](clone-element.md). |
--- | ||
id: react-ref | ||
title: React Ref | ||
@@ -17,4 +16,4 @@ --- | ||
let setSectionRef = (theRef, {ReasonReact.state}) => { | ||
state.mySectionRef := Js.Null.to_opt(theRef); | ||
/* wondering about Js.Null.to_opt? See the note below */ | ||
state.mySectionRef := Js.Nullable.to_opt(theRef); | ||
/* wondering about Js.Nullable.to_opt? See the note below */ | ||
}; | ||
@@ -32,5 +31,5 @@ | ||
Attaching to a React DOM element looks the same: `state.mySectionRef = {myDivRef: Js.Null.to_opt theRef}`. | ||
Attaching to a React DOM element looks the same: `state.mySectionRef = {myDivRef: Js.Nullable.to_opt(theRef)}`. | ||
**Note** how [ReactJS refs can be null](https://github.com/facebook/react/issues/9328#issuecomment-298438237). Which is why `theRef` and `myDivRef` are converted from a [JS nullable](http://bucklescript.github.io/bucklescript/Manual.html#_null_and_undefined) to an OCaml `option` (Some/None). When you use the ref, you'll be forced to handle the null case through a `switch`, which prevents subtle errors! | ||
**Note** how [ReactJS refs can be null](https://github.com/facebook/react/issues/9328#issuecomment-298438237). Which is why `theRef` and `myDivRef` are converted from a [JS nullable](https://bucklescript.github.io/docs/en/null-undefined-option.html) to an OCaml `option` (Some/None). When you use the ref, you'll be forced to handle the null case through a `switch`, which prevents subtle errors! | ||
@@ -37,0 +36,0 @@ **You must follow the instanceVars convention in the previous section for ref**. |
--- | ||
id: reason-using-js | ||
title: ReasonReact using ReactJS | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: render | ||
title: Render | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: retained-props | ||
title: Retained Props | ||
@@ -19,4 +18,4 @@ --- | ||
}, | ||
render: (_self) => <div> (ReasonReact.stringToElement(message)) </div> | ||
render: _self => <div>{ReasonReact.stringToElement(message)}</div> | ||
}; | ||
``` |
--- | ||
id: roadmap | ||
title: Roadmap & Contribution | ||
@@ -11,7 +10,11 @@ --- | ||
## Your Contribution Opportunities | ||
Wanna help? | ||
- Help us translate the docs! Click on the translation symbol on the upper right | ||
- Improve the documentation and website | ||
- [Suggest better error messages for ReasonReact](https://github.com/reasonml-community/error-message-improvement/issues) | ||
- Convert over the ReactJS components! With ReasonReact's [interop](./interop.md), you can actually keep your JS component library's API stable while provinding a better ReasonReact API | ||
- Help us translate the docs! Click on the translation symbol on the upper right. | ||
- Improve the documentation and website. | ||
- [Suggest better error messages for ReasonReact](https://github.com/reasonml-community/error-message-improvement/issues). | ||
- Convert over the ReactJS components! With ReasonReact's [interop](./interop.md), you can actually keep your JS component library's API stable while provinding a better ReasonReact API. |
--- | ||
id: simple | ||
title: Simple | ||
@@ -10,7 +9,9 @@ --- | ||
let component = ReasonReact.statelessComponent("Page"); | ||
let handleClick = (_event, _self) => Js.log("clicked!"); | ||
let make = (~message, _children) => { | ||
...component, | ||
render: (self) => | ||
<div onClick=(self.handle(handleClick))> (ReasonReact.stringToElement(message)) </div> | ||
render: self => | ||
<div onClick={self.handle(handleClick)}>{ReasonReact.stringToElement(message)}</div> | ||
}; | ||
@@ -17,0 +18,0 @@ ``` |
--- | ||
id: state-actions-reducer | ||
title: State, Actions & Reducer | ||
@@ -21,3 +20,3 @@ --- | ||
"Hello " ++ name ++ ". You've clicked the button " ++ string_of_int(self.state) ++ " time(s)!"; | ||
<div> {ReasonReact.stringToElement(greeting)} </div> | ||
<div>{ReasonReact.stringToElement(greeting)}</div> | ||
} | ||
@@ -93,7 +92,7 @@ }; | ||
render: (self) => { | ||
let message = "Clicked " ++ (string_of_int(self.state.count) ++ " times(s)"); | ||
let message = "Clicked " ++ string_of_int(self.state.count) ++ " times(s)"; | ||
<div> | ||
<MyDialog | ||
onClick=(self.reduce((_event) => Click)) | ||
onSubmit=(self.reduce((_event) => Toggle)) | ||
onClick={_event => self.send(Click)} | ||
onSubmit={_event => self.send(Toggle)} | ||
/> | ||
@@ -104,5 +103,6 @@ {ReasonReact.stringToElement(message)} | ||
}; | ||
``` | ||
**Note**: if you ever see mentions of `self.reduce`, this is the old API. The new API is called `self.send`. The old API's docs are [here](https://github.com/reasonml/reason-react/blob/e17fcb5d27a2b7fb2cfdc09d46f0b4cf765e50e4/docs/state-actions-reducer.md). | ||
A few things: | ||
@@ -113,8 +113,8 @@ | ||
- The current `state` value is accessible through `self.state`, whenever `self` is passed to you as an argument of some function. | ||
- A "**reducer**"! This [pattern-matches](https://reasonml.github.io/guide/language/pattern-matching) on the possible actions and specify what state update each action corresponds to. _In state machine terminology, this'd be a "state transition"_. | ||
- In `render`, instead of `self.handle` (which doesn't allow state updates), you'd use `self.reduce`. `reduce` takes a callback, passing it the event (or whatever callback payload `onSubmit`/`onClick`/`onFoo` gives you from `MyDialog`) and asking for an action as the return value. | ||
- A "**reducer**"! This [pattern-matches](https://reasonml.github.io/docs/en/pattern-matching.html) on the possible actions and specify what state update each action corresponds to. _In state machine terminology, this'd be a "state transition"_. | ||
- In `render`, instead of `self.handle` (which doesn't allow state updates), you'd use `self.send`. `send` takes an action. | ||
So, when a click on the dialog is triggered, we send the `Click` action to the reducer, which handles the `Click` case by returning the new state that increment a counter. ReasonReact takes the state and updates the component. | ||
So, when a click on the dialog is triggered, we "send" the `Click` action to the reducer, which handles the `Click` case by returning the new state that increment a counter. ReasonReact takes the state and updates the component. | ||
**Note**: just like for `self.handle`, sometimes you might be forwarding `reduce` to some helper functions. Pass the whole `self` instead and **annotate it**. This avoids a complex `self` record type behavior. See [Record Field `reduce`/`handle` Not Found](record-field-reduce-handle-not-found.md). | ||
**Note**: just like for `self.handle`, sometimes you might be forwarding `send` to some helper functions. Pass the whole `self` instead and **annotate it**. This avoids a complex `self` record type behavior. See [Record Field `send`/`handle` Not Found](record-field-send-handle-not-found.md). | ||
@@ -127,4 +127,4 @@ ## State Update Through Reducer | ||
- `ReasonReact.Update state`: update the state. | ||
- `ReasonReact.SideEffects((self) => unit)`: no state update, but trigger a side-effect, e.g. `ReasonReact.SideEffects((_self) => Js.log "hello!"))`. | ||
- `ReasonReact.UpdateWithSideEffects(state, (self) => unit)`: update the state, **then** trigger a side-effect. | ||
- `ReasonReact.SideEffects(self => unit)`: no state update, but trigger a side-effect, e.g. `ReasonReact.SideEffects(_self => Js.log("hello!"))`. | ||
- `ReasonReact.UpdateWithSideEffects(state, self => unit)`: update the state, **then** trigger a side-effect. | ||
@@ -137,11 +137,12 @@ _If you're a power user, there's also `SilentUpdate` and `SilentUpdateWithSideEffects`. See reasonReact.rei to see what they do. Don't use them if you're trying to update a ref/timer/subscription/any other instance variable_. | ||
- The `action` type's variants can carry a payload: `onClick=(self.reduce((data) => Click data.foo))`. | ||
- The `action` type's variants can carry a payload: `onClick={data => self.send(Click(data.foo))}`. | ||
- Don't pass the whole event into the action variant's payload. ReactJS events are pooled; by the time you intercept the action in the `reducer`, the event's already recycled. | ||
- `reducer` must be pure (not to be confused with `self.reduce`, which can be impure)! Don't do side-effects in them directly. You'll thank us when we enable the upcoming concurrent React (Fiber). Use `SideEffects` or `UpdateWithSideEffects` to enqueue a side-effect. The side-effect (the callback) will be executed after the state setting, but before the next render. | ||
- If you need to do e.g. `ReactEventRe.BlablaEvent.preventDefault event`, do it in `self.reduce`, before returning the action type. Again, `reducer` must be pure. | ||
- If your state only holds instance variables, it also means (by the convention in the instance variables section) that your component only contains `self.handle`, no `self.reduce`. You still needs to specify a `reducer` like so: `reducer:((), _state) => ReasonReact.NoUpdate`. Otherwise you'll get a `variable cannot be generalized` type error. | ||
- `reducer` **must** be pure! Aka don't do side-effects in them directly. You'll thank us when we enable the upcoming concurrent React (Fiber). Use `SideEffects` or `UpdateWithSideEffects` to enqueue a side-effect. The side-effect (the callback) will be executed after the state setting, but before the next render. | ||
- If you need to do e.g. `ReactEventRe.BlablaEvent.preventDefault(event)`, do it in `self.send`, before returning the action type. Again, `reducer` must be pure. | ||
- Feel free to trigger another action in `SideEffects` and `UpdateWithSideEffects`, e.g. `UpdateWithSideEffects(newState, (self) => self.send(Click))`. | ||
- If your state only holds instance variables, it also means (by the convention in the instance variables section) that your component only contains `self.handle`, no `self.send`. You still needs to specify a `reducer` like so: `reducer: ((), _state) => ReasonReact.NoUpdate`. Otherwise you'll get a `variable cannot be generalized` type error. | ||
### Tip | ||
Cram as much as possible into `reducer`. Keep your actual callback handlers (the `self.reduce((foo) => Bar))` part) dumb and small. This makes all your state updates & side-effects (which itself should mostly only be inside `ReasonReact.SideEffects` and `ReasonReact.UpdateWithSideEffects`) much easier to scan through. Also more ReactJS fiber async-mode resilient. | ||
Cram as much as possible into `reducer`. Keep your actual callback handlers (the `self.send(Foo)` part) dumb and small. This makes all your state updates & side-effects (which itself should mostly only be inside `ReasonReact.SideEffects` and `ReasonReact.UpdateWithSideEffects`) much easier to scan through. Also more ReactJS fiber async-mode resilient. | ||
@@ -156,33 +157,6 @@ ## Async State Setting | ||
In ReasonReact, since the new state, if any, is returned from the `reducer`, the above wouldn't work; naturally, returning a new state from a `setInterval` doesn't make sense! | ||
In ReasonReact, you'd do something similar: | ||
Instead, you'd do: | ||
```reason | ||
type action = | ||
| Tick; | ||
type state = { | ||
count: int, | ||
timerId: ref(option(Js.Global.intervalId)) | ||
}; | ||
let component = ReasonReact.reducerComponent("Counter"); | ||
let make = (_children) => { | ||
...component, | ||
initialState: () => {count: 0, timerId: ref(None)}, | ||
reducer: (action, state) => | ||
switch action { | ||
| Tick => ReasonReact.Update({...state, count: state.count + 1}) | ||
}, | ||
didMount: (self) => { | ||
self.state.timerId := Some(Js.Global.setInterval(self.reduce((_) => Tick), 1000)); | ||
ReasonReact.NoUpdate | ||
}, | ||
render: ({state}) => <div> {ReasonReact.stringToElement(string_of_int(state.count))} </div> | ||
}; | ||
Js.Global.setInterval(() => self.send(Tick), 1000) | ||
``` | ||
Aka, creating a `reducer` handler as you would normally, and let that setInterval (or whatver async state setting you use) asynchronously call the callback returned by your `self.reduce`. |
--- | ||
id: style | ||
title: Style | ||
@@ -4,0 +3,0 @@ --- |
--- | ||
id: ternary-shortcut | ||
title: Ternary Shortcut | ||
--- | ||
ReactJS allows the pattern `showButton && <Button />`. While in this specific case, it's usually not too harmful, in general, try not to do this. In ReasonReact, you use `showButton ? <Button /> : ReasonReact.nullElement`. | ||
ReactJS allows the pattern `showButton && <Button />`. While in this specific case, it's slightly shorter, in general, try not to do this in JS. In ReasonReact, you need to use `showButton ? <Button /> : ReasonReact.nullElement`. |
@@ -10,3 +10,3 @@ Like HISTORY.md, but for planned future versions and subject to change. The vocabulary here uses the past tense, to pretend that the versions are already published. | ||
# 0.3.1 | ||
# 0.3.2 | ||
@@ -13,0 +13,0 @@ - Preparation for namespace |
@@ -0,1 +1,15 @@ | ||
# 0.3.1 | ||
No breaking change. **The migration script is [here](https://github.com/chenglou/upgrade-reason-react-to-031)**. | ||
- New [subscriptions helper](https://reasonml.github.com/reason-react/docs/en/subscriptions-helper.html). | ||
- [**Router** is here](https://reasonml.github.com/reason-react/docs/en/router.html)! | ||
- `self.reduce` is now changed into `self.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}` | ||
# 0.3.0 | ||
@@ -7,6 +21,6 @@ | ||
- **Loosen `children`'s restriction**. This unlocks _huge_ potentials. See the blog post! | ||
- **Loosen `children`'s restriction**. This unlocks _huge_ potentials. See the [blog post](https://reasonml.github.io/reason-react/blog/2017/11/17/power-children.html)! | ||
- 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](https://reasonml.github.io/guide/meta/project-structure#file-casing). Technically an internal change; doesn't affect your usage of ReasonReact. | ||
- All files upper-cased. This follows the [new community idiom](https://reasonml.github.io/docs/en/project-structure.html#file-casing). 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. | ||
@@ -53,3 +67,3 @@ | ||
The relevant section on actions, reducers and the new update additions are [in the main docs](https://reasonml.github.io/reason-react/#reason-react-component-creation-state-actions-reducer). | ||
The relevant section on actions, reducers and the new update additions are [in the main docs](https://reasonml.github.io/reason-react/docs/en/state-actions-reducer.html). | ||
@@ -60,3 +74,3 @@ **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`](https://reasonml.github.io/guide/language/mutation)). See the updated [instance variables section](https://reasonml.github.io/reason-react/#reason-react-component-creation-instance-variables). | ||
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`](https://reasonml.github.io/docs/en/mutation.html)). See the updated [instance variables section](https://reasonml.github.io/reason-react/docs/en/instance-variables.html). | ||
@@ -162,3 +176,3 @@ The new recommendation also solves a corner-case bug with assigning more than one refs in the render. | ||
### `componentBag.props` | ||
Replaced with the new `make` (previously `createElement`) call which takes in labeled arguments. See more in [this section](https://reasonml.github.io/reason-react/#reason-react-component-creation-props). | ||
Replaced with the new `make` (previously `createElement`) call which takes in labeled arguments. See more in [this section](https://reasonml.github.io/reason-react/docs/en/creation-props-self.html). | ||
@@ -171,3 +185,3 @@ How to access `props` in the `update`/`handle` callbacks now? You'd move these callback definitions into the `make` function body. | ||
### `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`](https://reasonml.github.io/reason-react/#reason-react-component-creation-callback-handlers) (doesn't trigger re-render, but does update state) in callbacks & lifecycles. | ||
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`](https://reasonml.github.io/reason-react/docs/en/callback-handlers.html) (doesn't trigger re-render, but does update state) in callbacks & lifecycles. | ||
@@ -187,3 +201,3 @@ ### `componentBag.setState` | ||
## Render | ||
`render`, previously in your component module, is now inside `make`. See the example [here](https://reasonml.github.io/reason-react/#reason-react-intro-examples). | ||
`render`, previously in your component module, is now inside `make`. See the example [here](https://reasonml.github.io/reason-react/docs/en/intro-example.html). | ||
@@ -195,3 +209,3 @@ ## Ref & Other Mutable Instance Variables | ||
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](https://reasonml.github.io/reason-react/#reason-react-component-creation-lifecycle-events). | ||
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](https://reasonml.github.io/reason-react/docs/en/lifecycles.html). | ||
@@ -198,0 +212,0 @@ ## Children |
'use strict'; | ||
var List = require("bs-platform/lib/js/list.js"); | ||
var $$Array = require("bs-platform/lib/js/array.js"); | ||
var Curry = require("bs-platform/lib/js/curry.js"); | ||
@@ -69,2 +70,6 @@ var React = require("react"); | ||
function subscriptionsDefault() { | ||
return /* [] */0; | ||
} | ||
function convertPropsIfTheyreFromJs(props, jsPropsToReason, debugName) { | ||
@@ -89,2 +94,3 @@ var match = props.reasonProps; | ||
displayName: debugName, | ||
subscriptions: null, | ||
self: (function (state, retainedProps) { | ||
@@ -96,3 +102,4 @@ var $$this = this ; | ||
/* state */state, | ||
/* retainedProps */retainedProps | ||
/* retainedProps */retainedProps, | ||
/* send */$$this.sendMethod | ||
]; | ||
@@ -169,6 +176,16 @@ }), | ||
var component = convertedReasonProps[0]; | ||
var curTotalState = thisJs.state; | ||
var curReasonState = curTotalState.reasonState; | ||
var self = $$this.self(curReasonState, component[/* retainedProps */11]); | ||
if (component[/* subscriptions */13] !== subscriptionsDefault) { | ||
var subscriptions = List.map((function (param) { | ||
var unsubscribe = param[1]; | ||
var token = Curry._1(param[0], /* () */0); | ||
return (function () { | ||
return Curry._1(unsubscribe, token); | ||
}); | ||
}), Curry._1(component[/* subscriptions */13], self)); | ||
$$this.subscriptions = subscriptions; | ||
} | ||
if (component[/* didMount */4] !== lifecycleNoUpdate) { | ||
var curTotalState = thisJs.state; | ||
var curReasonState = curTotalState.reasonState; | ||
var self = $$this.self(curReasonState, component[/* retainedProps */11]); | ||
var reasonStateUpdate = Curry._1(component[/* didMount */4], self); | ||
@@ -201,2 +218,3 @@ var nextTotalState = $$this.transitionNextTotalState(curTotalState, reasonStateUpdate); | ||
var oldSelf_003 = /* retainedProps */oldConvertedReasonProps[0][/* retainedProps */11]; | ||
var oldSelf_004 = /* send */newSelf[/* send */4]; | ||
var oldSelf = /* record */[ | ||
@@ -206,3 +224,4 @@ oldSelf_000, | ||
/* state */prevReasonState, | ||
oldSelf_003 | ||
oldSelf_003, | ||
oldSelf_004 | ||
]; | ||
@@ -222,8 +241,14 @@ return Curry._1(newComponent[/* didUpdate */5], /* record */[ | ||
var component = convertedReasonProps[0]; | ||
var curState = thisJs.state; | ||
var curReasonState = curState.reasonState; | ||
if (component[/* willUnmount */6] !== lifecycleReturnUnit) { | ||
var curState = thisJs.state; | ||
var curReasonState = curState.reasonState; | ||
return Curry._1(component[/* willUnmount */6], $$this.self(curReasonState, component[/* retainedProps */11])); | ||
Curry._1(component[/* willUnmount */6], $$this.self(curReasonState, component[/* retainedProps */11])); | ||
} | ||
var match = $$this.subscriptions; | ||
if (match == null) { | ||
return /* () */0; | ||
} else { | ||
return 0; | ||
return List.iter((function (unsubscribe) { | ||
return Curry._1(unsubscribe, /* () */0); | ||
}), List.rev(match)); | ||
} | ||
@@ -247,2 +272,3 @@ }), | ||
var oldSelf_003 = /* retainedProps */oldConvertedReasonProps[0][/* retainedProps */11]; | ||
var oldSelf_004 = /* send */newSelf[/* send */4]; | ||
var oldSelf = /* record */[ | ||
@@ -252,3 +278,4 @@ oldSelf_000, | ||
/* state */curReasonState, | ||
oldSelf_003 | ||
oldSelf_003, | ||
oldSelf_004 | ||
]; | ||
@@ -317,2 +344,3 @@ return Curry._1(newComponent[/* willUpdate */7], /* record */[ | ||
var oldSelf_003 = /* retainedProps */oldConvertedReasonProps[0][/* retainedProps */11]; | ||
var oldSelf_004 = /* send */newSelf[/* send */4]; | ||
var oldSelf = /* record */[ | ||
@@ -322,3 +350,4 @@ oldSelf_000, | ||
/* state */curReasonState, | ||
oldSelf_003 | ||
oldSelf_003, | ||
oldSelf_004 | ||
]; | ||
@@ -404,29 +433,31 @@ ret = Curry._1(newComponent[/* shouldUpdate */8], /* record */[ | ||
}), | ||
reduceMethod: (function (callback) { | ||
sendMethod: (function (action) { | ||
var $$this = this ; | ||
var thisJs = (this); | ||
return (function ($$event) { | ||
var convertedReasonProps = convertPropsIfTheyreFromJs(thisJs.props, thisJs.jsPropsToReason, debugName); | ||
var component = convertedReasonProps[0]; | ||
if (component[/* reducer */12] !== reducerDefault) { | ||
var action = Curry._1(callback, $$event); | ||
return thisJs.setState((function (curTotalState, _) { | ||
var curReasonState = curTotalState.reasonState; | ||
var reasonStateUpdate = Curry._2(component[/* reducer */12], action, curReasonState); | ||
if (reasonStateUpdate) { | ||
var nextTotalState = $$this.transitionNextTotalState(curTotalState, reasonStateUpdate); | ||
if (nextTotalState.reasonStateVersion !== curTotalState.reasonStateVersion) { | ||
return nextTotalState; | ||
} else { | ||
return magicNull; | ||
} | ||
} else { | ||
return magicNull; | ||
} | ||
})); | ||
} else { | ||
return 0; | ||
} | ||
}); | ||
var convertedReasonProps = convertPropsIfTheyreFromJs(thisJs.props, thisJs.jsPropsToReason, debugName); | ||
var component = convertedReasonProps[0]; | ||
if (component[/* reducer */12] !== reducerDefault) { | ||
var partialStateApplication = Curry._1(component[/* reducer */12], action); | ||
return thisJs.setState((function (curTotalState, _) { | ||
var curReasonState = curTotalState.reasonState; | ||
var reasonStateUpdate = Curry._1(partialStateApplication, curReasonState); | ||
if (reasonStateUpdate) { | ||
var nextTotalState = $$this.transitionNextTotalState(curTotalState, reasonStateUpdate); | ||
if (nextTotalState.reasonStateVersion !== curTotalState.reasonStateVersion) { | ||
return nextTotalState; | ||
} else { | ||
return magicNull; | ||
} | ||
} else { | ||
return magicNull; | ||
} | ||
})); | ||
} else { | ||
return 0; | ||
} | ||
}), | ||
reduceMethod: (function (callback, payload) { | ||
var $$this = this ; | ||
return $$this.sendMethod(Curry._1(callback, payload)); | ||
}), | ||
render: (function () { | ||
@@ -459,2 +490,3 @@ var $$this = this ; | ||
/* reducer */reducerDefault, | ||
/* subscriptions */subscriptionsDefault, | ||
/* jsElementWrapped : None */0 | ||
@@ -476,3 +508,3 @@ ]; | ||
var element$1 = /* Element */[component]; | ||
var match = component[/* jsElementWrapped */13]; | ||
var match = component[/* jsElementWrapped */14]; | ||
if (match) { | ||
@@ -515,6 +547,104 @@ return Curry._2(match[0], key, ref); | ||
var newrecord = dummyInteropComponent.slice(); | ||
newrecord[/* jsElementWrapped */13] = jsElementWrapped; | ||
newrecord[/* jsElementWrapped */14] = jsElementWrapped; | ||
return newrecord; | ||
} | ||
function path() { | ||
var match = typeof (window) === "undefined" ? undefined : (window); | ||
if (match !== undefined) { | ||
var raw = match.location.pathname; | ||
switch (raw) { | ||
case "" : | ||
case "/" : | ||
return /* [] */0; | ||
default: | ||
var raw$1 = raw.slice(1); | ||
var match$1 = raw$1[raw$1.length - 1 | 0]; | ||
var raw$2 = match$1 === "/" ? raw$1.slice(0, -1) : raw$1; | ||
return $$Array.to_list(raw$2.split("/")); | ||
} | ||
} else { | ||
return /* [] */0; | ||
} | ||
} | ||
function hash() { | ||
var match = typeof (window) === "undefined" ? undefined : (window); | ||
if (match !== undefined) { | ||
var raw = match.location.hash; | ||
switch (raw) { | ||
case "" : | ||
case "#" : | ||
return ""; | ||
default: | ||
return raw.slice(1); | ||
} | ||
} else { | ||
return ""; | ||
} | ||
} | ||
function search() { | ||
var match = typeof (window) === "undefined" ? undefined : (window); | ||
if (match !== undefined) { | ||
var raw = match.location.search; | ||
switch (raw) { | ||
case "" : | ||
case "?" : | ||
return ""; | ||
default: | ||
return raw.slice(1); | ||
} | ||
} else { | ||
return ""; | ||
} | ||
} | ||
function push(path) { | ||
var match = typeof (history) === "undefined" ? undefined : (history); | ||
var match$1 = typeof (window) === "undefined" ? undefined : (window); | ||
if (match !== undefined && match$1 !== undefined) { | ||
match.pushState((null), "", path); | ||
match$1.dispatchEvent(new Event("popstate")); | ||
return /* () */0; | ||
} else { | ||
return /* () */0; | ||
} | ||
} | ||
function watchUrl(callback) { | ||
var match = typeof (window) === "undefined" ? undefined : (window); | ||
if (match !== undefined) { | ||
var watcherID = function () { | ||
return Curry._1(callback, /* record */[ | ||
/* path */path(/* () */0), | ||
/* hash */hash(/* () */0), | ||
/* search */search(/* () */0) | ||
]); | ||
}; | ||
match.addEventListener("popstate", watcherID); | ||
return watcherID; | ||
} else { | ||
return (function () { | ||
return /* () */0; | ||
}); | ||
} | ||
} | ||
function unwatchUrl(watcherID) { | ||
var match = typeof (window) === "undefined" ? undefined : (window); | ||
if (match !== undefined) { | ||
match.removeEventListener("popstate", watcherID); | ||
return /* () */0; | ||
} else { | ||
return /* () */0; | ||
} | ||
} | ||
var Router = [ | ||
push, | ||
watchUrl, | ||
unwatchUrl | ||
]; | ||
exports.Callback = Callback; | ||
@@ -529,2 +659,3 @@ exports.statelessComponent = statelessComponent; | ||
exports.wrapJsForReason = wrapJsForReason; | ||
exports.Router = Router; | ||
/* magicNull Not a pure module */ |
{ | ||
"name": "reason-react", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "", | ||
@@ -12,3 +12,7 @@ "main": "index.js", | ||
}, | ||
"keywords": [], | ||
"keywords": [ | ||
"reasonml", | ||
"bucklescript", | ||
"react" | ||
], | ||
"author": "", | ||
@@ -21,3 +25,3 @@ "license": "MIT", | ||
"devDependencies": { | ||
"bs-platform": "^2.0.0" | ||
"bs-platform": "^2.1.0" | ||
}, | ||
@@ -24,0 +28,0 @@ "dependencies": { |
@@ -12,2 +12,3 @@ # ReasonReact | ||
## Usage | ||
See https://github.com/reasonml-community/reason-react-example | ||
@@ -14,0 +15,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
3049990
124
0
1679
31
44