reasonml / reason-react Goto Github PK
View Code? Open in Web Editor NEWReason bindings for ReactJS
Home Page: https://reasonml.github.io/reason-react/
License: MIT License
Reason bindings for ReactJS
Home Page: https://reasonml.github.io/reason-react/
License: MIT License
Hi,
I am trying out ReasonML. Did the global installation and bs-platform.
Created the ap using bsb -init my-first-app -theme basic-reason.
But when i try out the examples i get error above.
example: let x = "Hello" ++ "World";
`> [email protected] start /Users/prashanth/workspace/reasonml/my-first-app
bsb -make-world -w
Start compiling
Rebuilding since just get started
ninja: Entering directorylib/bs' ninja: no work to do. Finish compiling Start compiling Rebuilding since [ [ 'change', 'demo.re' ] ] ninja: Entering directory
lib/bs'
[2/2] Building src/demo.mlast.d
[1/1] Building src/demo-MyFirstApp.cmj
FAILED: src/demo-MyFirstApp.cmj /Users/prashanth/workspace/reasonml/my-first-app/lib/js/src/demo.js src/demo-MyFirstApp.cmi
/usr/local/lib/node_modules/bs-platform/bin/bsc.exe -bs-package-map my-first-app -bs-package-output commonjs:lib/js/src -bs-assume-no-mli -bs-no-builtin-ppx-ml -bs-no-implicit-include -I . -I src -w -30-40+6+7+27+32..39+44+45+101 -nostdlib -I '/Users/prashanth/workspace/reasonml/my-first-app/node_modules/bs-platform/lib/ocaml' -no-alias-deps -color always -bs-re-out -bs-super-errors -o src/demo-MyFirstApp.cmj -c src/demo.mlast
We've found a bug for you!
(No file name)
The value ++ can't be found
ninja: build stopped: subcommand failed.
Finish compiling(exit: 1)
`
In React Server side rendering, componentDidMount
is not triggered.
On the other hand, ReasonReact doesn't have willMount
but only didMount
.
What can I do if I need componentDidMount
in server side?
Component example to reproduce the behavior
type state = unit;
type actions = | A | B;
let component = ReasonReact.reducerComponent "Test";
let make _ => {
...component,
initialState: fun () => (),
reducer: fun action _state => switch action {
| A => {
Js.log "A action triggered";
ReasonReact.SideEffects (fun { reduce } => {
Js.log "A side effects triggered";
/* Doesn't work as expected */
reduce (fun _ => B) ();
/* Works as expected */
/* Js.Global.setTimeout (fun _ => {
reduce (fun _ => B) ();
}) 0
|> ignore; */
})
}
| B => {
Js.log "B action triggered";
ReasonReact.SideEffects (fun _ => {
Js.log "B side effects triggered"
})
}
},
render: fun self => {
let _state: unit = self.state;
<div>
<button onClick=(self.reduce (fun _ => A))>(ReasonReact.stringToElement "Trigger A")</button>
<button onClick=(self.reduce (fun _ => B))>(ReasonReact.stringToElement "Trigger B")</button>
</div>
}
}
When you trigger action B directly it works correctly.
When you trigger action A it triggers both console logs from action A and the first one from action B, but B's side effects are never triggered.
Also on react 15, when you click A, then the B action's first console log will only get triggered on the next action. So when you press A you'd get logs
A action triggered
A side effects triggered
And when you press A again all console logs become (same kind of behavior on triggering A and then B)
A action triggered
A side effects triggered
B action triggered
A action triggered
A side effects triggered
So the B action is only handled when next action is triggered (but before it) and side effects are still ignored.
When you add timeout to triggering action B it all works as expected
Versions:
bs-platform 1.9.3
reason-react 0.2.4
OS: Ubuntu 16.04
When you do interop with JS (also applies to working with children) you are forced to always pass props to a component, however some components are designed not to take any props and then you are forced to pass empty object. This is both inconvenient and seems like a source of problems for beginners, so I think the argument should be made optional.
Carried over from reasonml-old/reason-react#79. cc @saschatimme
Reason React currently defines style in ReactDOMRe. In bs-react-native we have also defined styles.
Now we have a problem if some component is cross-platform (e.g. react-navigation) and takes style as a prop (for which style would you write the bindings?).
I would propose to move the style type from ReactDOMRe to ReactRe. Then we could use the same style in both projects.
The React component rendered on the simple
page of this repo exhibits different behavior when installed with NPM (v4 and v5) and Yarn. With NPM the first click shows:
Click count: 0
Switch state: on
With Yarn it shows (correctly):
Click count: 1
Switch state: on
The click count is updated in a side effect. There are no obvious differences between toplevel dependencies on NPM/Yarn.
https://github.com/rickyvetter/reason-react-example/tree/npmyarn
since npm expects semver it reduces the chance of new version breaking a build
I have followed this https://reasonml.github.io/guide/editor-tools, and my VSCode could type check the Reason Native project https://github.com/reasonml/ReasonNativeProject (with minor glitch reasonml-editor/vscode-reasonml#76 (comment))
However, for this project, there are still a lot of "Unbound module errors": Js
, ReasonReact
. And no .merlin file exists here.
How to correctly setup Merlin for Bucklescript project? It seems Js
and ReasonReact
bindings are not even in opam
?
We'll add this back if demands are high
cc @jordwalke @rickyvetter for elaboration on this =)
let comp = ReasonReact.statelessComponent "Container";
let make children => {
...comp,
render: fun _ => <div> (children |> ReasonReact.arrayToElement) </div>
};
Will raise
Warning: Each child in an array or iterator should have a unique "key" prop.
in Chrome console.
In react-react, I have an abstraction on creating ReasonReact components. Each component looks like the following:
module FooComponent = {
type props = {name: string};
let vdomS propsS => S.map eq::(fun _ _ => false)
(fun {name} => <p> (ReasonReact.stringToElement name) </p>) propsS;
let component = ReasonReact.reducerComponent "FooComponent";
let make ::name _children => ReasonReact.componentFromSignal component {name} vdomS;
};
So, there's always 3 user-provided parameters:
ReasonReact.reducerComponent
)If this could be a functor, so I could abstract my API even further to look like this:
module FooComponent = GenerateComponent {
let name = "FooComponent";
type props = {name: string};
let vdomS propsS => S.map eq::(fun _ _ => false)
(fun {name} => <p> (ReasonReact.stringToElement name) </p>) propsS;
};
And the rest would be "magic".
But make
can't be generalized like that. Each make
function that takes different props will have completely different type signatures from each other.
But if JSX could look for a make_
function that, instead of using named parameters, used structs (optional parameters would be represented by fields with the option
type, of course), I could implement GenerateComponent
like the following:
module type COMPONENT = {
open ReactFrp.React;
let name: string;
type props;
let vdomS: signal props => signal ReasonReact.reactElement;
};
module GenerateComponent(C: COMPONENT) => {
let component = ReasonReact.reducerComponent C.name;
let make_ (props: C.props) _children => ReactReact.componentFromSignal component props C.vdomS;
};
Surely this would benefit more libraries than just react-react. Everyone who wants to abstract away details in the creation of a ReasonReact component could benefit from it. So, do you think the proposal makes sense?
Currently when you select something in a dropdown component, reason-react event handler expects to receive "event" object of type reactEventRe to kinda emulate JS event object. But third-party library can not always return event object, but just values.
So in JS words, for example
instead of this
const onClick = (e) => e;
return
const onClick = (e) => e.target.value;
So then all this assumption on reason-react of getting event type makes it impossible to use without hacks.
Example:
https://raw.githubusercontent.com/acierto/ts-explorer/master/src/core/advancedFilter/advancedFilter.re lines 28-29.
I'm receiving back from react-select lib onChange event with data {value: 'bla', 'label': bla}. But reason-react expects there reactEventRe type. So I don't know another hack as only by means of JS interops map type to my structure.
This project works, so you can easily reproduce that problem there.
From the ReasonReact docs:
ReasonReact doesnβt support ReactJS context (yet).
What needs to take place in order for ReasonReact to support context? Are we waiting for the context API to become stable before adding in support for it?
cc @rickyvetter
This is working:
let component = ReasonReact.statefulComponent "Greeting";
let make _children => {
...component,
initialState: fun () => "",
render: fun self => {
<div> (ReasonReact.stringToElement ( self.state)) </div>
}
};
This is not:
let component = ReasonReact.statefulComponent "Greeting";
type state = {
inputText: string
};
let make _children => {
...component,
initialState: fun () => {
inputText: ""
},
render: fun self => {
<div> (ReasonReact.stringToElement ( self.state.inputText)) </div>
}
};
Aka it'd be nice if <div />
post-ppx compiled to something like DOM.createElement("div", ...)
rather than ReactDOMRe.createElement("div", ...)
. It's only a name change; but this allows alternatives such as Preact to override the DOM
part without giving the impression that these are from ReasonReact.
Technically you could just use the pre-ppx desugared JSX: https://reasonml.github.io/guide/language/jsx
but writing one's own ppx + distribute it for js compilation isn't straightforward yet, and discouraged (macros and all)
As mentioned on Discord.
Even if they're manually added to the js props object, they will be overwritten by wrapProps
here: https://github.com/reasonml/reason-react/blob/master/src/reasonReact.re#L551
The simplest solution might be to add ::ref
and ::key
to wrapJsForReason
and pass them on to wrapProps
You have to do (reduce (fun () => MyAction)) ()
. Would be nice to have a reduceNonCallback
(that's a terrible name) or something...
Both in docs/props-spread.md
and in docs/clone-element.md
there is a dead link to props-spread, is this section deliberately removed from the docs? Or has just been moved? If that is the case, I would really want to prove a PR helping to fix the docs! π
@rickyvetter it's just a matter of adding crowdin as a devDep right?
npm run crowdin-download
Support for function components is on the roadmap, according to the docs, but I didn't find any existing issue I could track, so I'm making one now. π
Most recommendations put the optimal line length at 45-75 characters (See this for example). The line length in the docs are currently about 2-3 times as long at 120+ characters, making it a bit to read.
When the action type is defined after the component, it will give the error:
Is this a ReasonReact component with state?
If so, is the state type declared _after_ the component declaration?
Moving the state type before the declaration should resolved this!
It should be
Is this a ReasonReact component with state?
If so, is the state or action type declared _after_ the component declaration?
Moving the state and action type before the declaration should resolved this!
It seems that bsb only provides build-in react support via reason syntax
Here are some I made :D
make
function that just returns jsx (not the component spread)make
function that doesn't have a final _children
arg (the error is super inscrutable)When trying to compile Reason React with Rollup, the error
Error: Cannot call a namespace ('CreateClassInternalHack')
is thrown when compiling reason-react/lib/es6/src/reasonReact.js
import * as CreateClassInternalHack from "create-react-class";
is the problem, because trying to import the module's exports with import *
, but create-react-class
is not a real ES6 module, and only exports a function. When CreateClassInternalHack
is called as a function, that's invalid as the result of import *
should be an object.
Instead the import should ideally just import the default export:
import CreateClassInternalHack from "create-react-class";
However it might be difficult to get BuckleScript to produce this output. An alternative might be to define a wrapper JS module for create-react-class
which exports this function in a way which is compatible with the import that BuckleScript produces.
π Not sure if this is the right place to put this, but I'm just starting to use reason-react and I feel like a guided tutorial would be a nice way of getting introduced to the ecosystem.
Angular has this really nice Tour of Heroes tutorial. It's is a relatively comprehensive guide that goes through a lot of the Angular core and its best practices. I'm not very familiar with react-reason (or reasonml in general) and I'm wondering if something like the Tour of Heroes exists or if someone is working on something similar. If not, I'd love to star this effort.
Copied from reasonml-old/reason-react-example#21 (wrong place)
Reading the ReasonReact.re module has been enlightening as to how to create type safe bindings to a fairly complex and dynamic javascript library. Definitely awesome work by the team to get this far (and make the interface extremely simple and flexible).
I have done an experiment with part of my companies code base that shows that Reason can be trivially integrated and used to provide type safety to our react components. As part of the feedback of this experiment, one of my colleagues asked how to debug a Reason components props?
While the bucklescript output is easy to add breakpoints to, there are also other tools that can help debug a react component, particularly in more complex scenarios. I am talking specifically about the React Developer Tools for Chrome specifically: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi , but I am sure there are other tools that make use of the react library to provide other information that can help a user of react identify problem components.
I have attached some screenshots of how Reason components are rendered in the React Developer Tools for the snippet of Reason posted below, and as should be evident, the debugability of the FA component much lower than that of the LikeStatus component, primarily because the reason props are opaque to the rest of the system.
module FontAwesome = {
let component = ReasonReact.statelessComponent "FA";
let make name::name _children => {
...component,
render: fun () _ => {
<i className={"icon fa fa fa-" ^ name} />
}
};
};
let component = ReasonReact.statelessComponent "LikeStatus";
let make numLikes::numLikes ::isLiked ::canLike _children => {
...component,
render: fun () _ => {
ReactDOMRe.createElement "like-status" [|
<span className="num-likes">{ReactRe.stringToElement (string_of_int n ^ " people like this")}</span>,
if isLiked {
<span className="unlike">
<FontAwesome name="thumbs-up" />
<span>{ReactRe.stringToElement "Unlike"}</span>
</span>
} else {
/* !isLiked */
if canLike {
<span className="like">
<FontAwesome name="thumbs-o-up" />
<span>{ReactRe.stringToElement "Like"}</span>
</span>
} else {
ReactRe.nullElement
}
}
|]
}
};
let comp =
ReasonReact.wrapReasonForJs
::component
(fun jsProps =>
make
numLikes::jsProps##numLikes
isLiked::(Js.to_bool jsProps##isLiked)
canLike::(Js.to_bool jsProps##canLike) [||]
)
I propose a debugProps
feature in componentSpec
where a component can return what it considers to be it's props as a Js object that is merged with the props next to "reasonProps" (specifically here:
reason-react/src/reasonReact.re
Line 552 in 402225c
Let me know what you think.
Given this (heavily reduced) component:
let make ::count ::onChange _children => {
let handleClick _ => {
onChange (count + 1)
};
{
...component,
didMount: fun self => {
/* assume `element` has been acquired by legit means */
element##addEventListener "click" handleClick;
ReasonReact.NoUpdate
},
render: fun self =>
...
}
};
onChange
will never be called with anything other than 1
, regardless of what the count
prop is changed to. The reason is of course that make
will be called again on props change, which will define a new handleClick
function that closes over the new prop value, but will not (and obviously should not) call didMount
again to attach the updated event handler. So the old event handler with the old prop value will be called in perpetuity.
This isn't all that surprising if you understand how RR actually works, but it is if you expect it to work like reactjs' this.props
(which of course I did, even though I should know better). RR creates the illusion, and thereby expectation, that it does work like this.props
, until of course it doesn't. I suggest that this is made clear in the documentation somewhere, even if just as a FAQ.
There are two workarounds, neither of which are very nice:
didUpdate
. This would also involve maintaining state to keep a reference of the currently attached event handler for removal, but is a more contained solution and good practice regardless of needing updated props, in case the element is re-inserted.This seems like a fundamental restriction with the API as it currently is, but I wanted to bring it up so perhaps it can be addressed in some future iteration at least.
Full example here: https://github.com/glennsl/rr-stale-props-issue/blob/abb81215857d4240b5bfd748dbb28e4d98590d33/src/index.re
We can take the occasion to shim these.
Unifies render
, renderToElementWithClassName
and renderToElementWithId
type containerType = [ | `element(Dom.element) | `className(string) | `id(string)];
let render = (reactElement, container: containerType) =>
switch container {
| `element(element) => _render(reactElement, element)
| `className(className) =>
switch (getElementsByClassName(className)) {
| [||] =>
raise(
Invalid_argument(
"ReasonReact.DOM.render: no element of class " ++ className ++ " found in the HTML."
)
)
| [|element|] => _render(reactElement, element)
| elements => _render(reactElement, elements[0])
}
| `id(id) =>
switch (getElementById(id)) {
| None =>
raise(
Invalid_argument(
"ReasonReact.DOM.render: no element of id " ++ id ++ " found in the HTML."
)
)
| Some(element) => _render(reactElement, element)
}
};
Poly variants might be beginner unfriendly until we document them clearly.
Normal variants aren't that friendly because you need to qualify them.
Redux defines a reducer as:
function reducer(state, action)
But statefulComponent uses:
reducer: fun action state => {}
Is this deliberate to enable currying? Because it really tripped me up!
The current document way to use existing JavaScript components (https://github.com/chenglou/reason-react-example/blob/master/src/interop/myBannerRe.re) needs a lot of boilerplate code when you have long lists of props and lots of components.
I wonder if there is a more concise way using a mechanism similar to the external props in https://github.com/reasonml/reason-react/blob/master/src/reactDOMRe.re#L58.
We should look into first-classing as much as possible here, but once we've gotten to where we are comfortable we should document how deal with various sub/release apis. They very widely and some are more friendly to our React patterns than others.
@rickyvetter can you write a brief explanation here? I'll integrate it into the site after the next release
ReasonReact.update
is deprecated since 0.2.4, and the only way to update non-ref state is ReasonReact.reduce
. The bad part of ReasonReact.reduce
is you must define an action, which sometimes is annoying and not neccessary.
π on the new reducer API, it looks great!
One use-case from Redux that I would love to see enabled is centralized logging of actions; it can be tremendously useful for debugging. In Redux the way to do this is with a logging middleware, a generic function that all actions pass through on their way to the reducer. Analytics are another widely useful application for middleware, and this API could potentially allow actions to be transformed on their way through the middleware.
In Discord, @rickyvetter suggested adding an optional reducerHook
argument when creating a component. Then you could create your own component "factory" as follows:
let awesomeLogger action => {
Js.log action;
action
}
let reducerComponentWithAwesomeLogging
debugName
:componentSpec 'state stateless noRetainedProps noRetainedProps 'action =>
basicComponent reducerHook::awesomeLogger debugName;
Obviously logging a plain variant with Js.log
as above is not going to be super useful, it's just there to show the concept and a potential action => action
middleware signature
I've seen it's a common type error, but the link to the solution isn't clear:
https://reasonml.github.io/reason-react/index.html#reason-react-working-with-children
This was causing my error
<RenderList todos/>
(type state { todos: list todo }
)
but if I replace it with RenderList.make todos children
I have the following error:
Type todo is not compatible with type string
that seem to be because of my RenderList component :
let make ::todos _children => { /**/}
Can I type the todos here?
Another error I have is when I embed the component in other DOM elements:
render: fun => {
<div>
(RenderList.make todos children)
</div>
}
I get the error: Unbound module RenderList
That was a temporary measure.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.