- just a record
- need a
make
function - creates new component every JSX invocation
- props = labeled arguments
make
, last prop must bechilren
switch (ageFromProps) {
| None => <Foo name="Reason" />
| Some(nonNullableAge) => <Foo name="Reason" age=nonNullableAge />
Corrent, but shorthand is:
<Foo name="Reason" age=?agefromProps>
state
retainedprops
handle
reduce
^ Big-bag-of-information passed to lifecycle events/render/ + a few others ???
- Reason comes with JSX syntx (binding???)
Are fully typed - accepts any data structure the receive component accepts
<A> <div /> <div /> </A>
is effectively
<A> ([| <div />, <div /> |
])</A>
(!!! I think, example has array assigned as a variable )
But that won't work, as children are wrapped,
let kids = [| <div />, <div /> |];
ReasonReact.element(
A.make([| kids |])
);
To avoid double wrapping spread <A> ...kids </A>
Due to strict typing, need these:
.nullElement
.stringToElement
arrayToElement
let make = (~onClick, children) =>
...
render: (self) => <button onCLick=onClick />
To use an externally defined function which has access to self
, you need to use self.handle(<fn>)
elt component = ..
let make = (~onClick, _ch) => {
let click = (e, self) => {
onClick(e);
Js.log(self.state);
};
{
...component,
initialState: ...,
redner: (self) => <button onClick={self.handle(click)} />
},
??? I'm a bit confused why you wouldn't use a higher-order
let click => state => (e) =>
...
render: (self) => <button onClick={click(state)} />
self.handle
:
- single payoad, plus arg self
- returns
unit
??? Wait...wat
^ WRONG this list is the behavior of the callback in the example !!!!
- Don't pass
handle
to helper functions, pass the wholeself
and annotate it ??? - avoids complexity self.handle
is curried withcallback, cbPayload
(e.g.onClick
andevent
). Usually only ask foronClick
(partial now???), and then takescbPayload
and executes func body.- Multiple arguments = wrap in IIFE that creates tuple, and pass that
self.handle((all) => doClick(all))(one, two, three)
- Want state update? CANT USE self.handle, must use self.send
Stateful components
Like ReactJS stateful, + reducer (like redux). aka state-machine.
let component = ReasonReact.reducerComponent("Name");
let make = (~name, _children) => {
...component,
initialState: () => 0,
render: (self) => {
let greeting = name ++ " counter:" ++ self.state;
<div>{ReasonReact.stringToElement(greeting)}</div>
}
};
Declare state type (type state = {counter: int, showPopUp: bool};
right before ReasonReact.reducerComponent
call.
Only change state in reducer with pattern matcher:
type action =
| Click
| Toggle;
type state = {
count: int,
show: bool
};
let component - ReasonReact.reducerComponent("MyForm");
/* .... */
reducer: (action, state) =>
switch (action) {
| Click => ReasonReact.Update({ ...state, count: state.count +1 })
| Toggle => ReasonReact.Update({ ...state, is: !state.is })
};
- User define
action
is named by convention - variant of all state transitions (aka sm "token") - reducer (aka sm "state transition")
- in
render
, instead ofself.handle
(nostate
updates, use
self.sendtakes an action ???? not
reduce`???
Alternatives to ReasonReact.Update
:
NoUpdate
SideEffects(self => unit)
, no state, but some side-effectUpdateWithSideEffects(state, self => unit)
SilentUpdate
/SilentUpdateWithSideEffects
(!!! seems to defer, if regular update enqueued)
action
has payload- Don't pass event as payload - pooling could recycle in async reduction/state update
- reducers pure
- Event.preventDefault(Event) must happen in
self.send
BEFORE returning action type (??? event pooling) - Valid to trigger different action (!!! thunky) in
SideEffects
- No instance variables (??? e.g. initialState?/??)
self
contains onlyhandle
, notsend
. Replace with unitreducer: ((), state) => ReasonReact.NoUpdate
. Else compile time error
- Cram much into reducer
- lean callback handlers
Thus state updates/side-effects co-located
Js.Global.setInterval(() => self.send(Tick), 1000)
- no
willMount
willUpdate
/shouldUpdate
take record{oldSelf, newSelf}
Note: return ReasonReact.NoUpdate
whenever possible from lifecycle events (???hooks)
retainedProps
is persisted previous prop.
didUpdate: ({ oldSelf, newSelf }) =>
if (oldSelf.retainedProps.message !== newSelf.retainedProps.message) {
Js.log("message changed")
willReceiveProps
nextprops
are labeled arguments in make, current props areoldSelf.retainedProp
etc etc
ReactJS effectively mutation without re-render. Reason has a place for these
type state = {
blah: option(string),
rah: ref(option(int))
}
let component...
let make...
initialState: () => { blah: Some("Blah!", rah: ref(None)},
didMount: ({ state }) => {
state.rach := Some(Js.Global.setInterval(...));
ReasonReact.NoUpdate
},
....
Ceremony is prep for concurrent react ??? this link
Not "Reason ref
" for mutation, like a this.myVar
instance variable in ReactJS???
type state = {
isOpen: bool,
mySectionRef: ref(option(ReasonReact.reactRef))
};
let setSectionRef = (theRef, {ReasonReact.state}) => {
state.mySectionRef := Js.Nullable.to_opt(theRef);
/* wondering about Js.Nullable.to_opt? See the note below */
};
let component = ReasonReact.reducerComponent("MyPanel");
let make = (~className="", _children) => {
...component,
initialState: () => {isOpen: false, mySectionRef: ref(None)},
reducer: ...,
render: (self) => <Section1 ref={self.handle(setSectionRef)} />
};
!!! oh on its just equiv of <div ref={ ref => this.theRef = ref} />
- nullable
- escape hatch to allow acces to
React.Component
methods
let handleClick = (event, self) =>
switch (self.state.mySectionRef^) {
| None => ()
| Some(r) => ReasonReact.refToJsObj(r)##someMethod(1, 2, 3) /* I solemnly swear that I am up to no good */
};
??? e.g. testing?
They reckon its easy.
- Reason - wrap
- ReactJS - wrap, then import
Yeah looks fucking easy
....dayam. Shit is tight.
Map to ReactJS synthetic events - accessing values
ReactDOMRe.domElementToObj(ReactEventRe.Form.target(event))##value;
shrug
Some funky BuckleScript FFI accessors
-_-
<div style=(
ReactDOMRe.Style.make(~color="#444444", ~fontSize="68px", ())
)/>
Style objects are a valid base, but I just want styled-components link
- Same as ReactJS, but adding props use case would be covered by currying (??? ....via JSX I assume...)
- *Do need for data- **
<div data-payload=1 aria-label="click me" className="foo" /> /* INVALID function label
For example, data-* and aria-* attributes aren't syntactically valid as a function label
??? labelled arguments = function label (or function signature?)
let myElement =
ReasonReact.cloneElement(
<div className="foo" />,
~props={"data-payload": 1, "aria-label": "click me"},
[||]
);
- exact control over children type
- Some restrictions on type constraints:
- DOM elements must use array, lest wierd data types get fwded to underlying ReactJS div/span
- User elements, default to array. Silly constraint of JSX syntax
Auto array wrapping. Need to spread.
``
<MyForm> ...(<div />, <div />) </MyForm>
<Motion> ...((name) => <div className=name />) </Motion>
<Layout> ...(ThreeRows(<div />, child2, child3)) </Layout> /* TwoRows | ThreeRows | FourColumns ???? slightly strange */
This doesnt work for dom elements
et children = [| <div /> |];
<div> ...children </div>; /* <--- this line */
(bindings related constraints)
ReasonReact's DOM module = ReactDOMRe
Has helpers, e.g.:
render
:(ReasonReact.reactElement, Dom.element) => unit
unmountComponentAtNode:Dom.element => unit
findDOMNode
:ReasonReact.reactRef => Dom.element
objToDOMProps
:Js.t({...}) => reactDOMProps
use casedomElementToObj
: turns into js object which allows u to dangerously access And:renderToElementWithClassName
:(ReasonReact.reactElement, string) => unit
finds first el of provided class and render to it????renderToElementWithId
:(ReasonReact.reactElement, string) => unit
finds element and render(s???) to it
If you have:
- legacy or interop data sources from outside React/ReasonReact tree
- a timer
- browser event handling You'd listen and react to these changes by, for instance, updating state
i.e. with Js.Global.setInterval
with ref(option(Js.Global.intervalId))
, self.state.timerId^
<carat
A bit messy
let component = ReasonReact.statelessComponent("Todo");
let make = (_children) => {
...component,
subscriptions: (self) => [
Sub(
() => Js.Global.setInterval(() => Js.log("hello!"), 1000), /* fn returns "subscription token" */
Js.Global.clearInterval /* fn, given the token (??? it executes the fn), does side-effect - probs clean sub
)
],
render: ...
}
Simple, thin, etc
Uses pattern matching
Can use subscriptions to observe url "location"
let component = ReasonReact.reducerComponent("TodoApp");
let make = (_children) => {
...component,
reducer: (action, state) =>
switch (action) {
/* router actions */
| ShowAll => ReasonReact.Update({...state, nowShowing: AllTodos})
| ShowActive => ...
/* todo actions */
| ChangeTodo(text) => ...
},
subscriptions: (self) => [
Sub(
() =>
ReasonReact.Router.watchUrl(
url => {
switch (url.hash, MyAppStatus.isUserLoggedIn) {
| ("active", _) => self.send(ShowActive)
| ("completed", _) => self.send(ShowCompleted)
| ("shared", true) => self.send(ShowShared)
| ("shared", false) when isSpecialUser => ... /* handle this state */
| ("shared", false) => ... /* handle this state */
| _ => self.send(ShowAll)
}
}
),
ReasonReact.Router.unwatchUrl
)
],
render: ...
}
<input _type
type
is reservered word
<div data-blah aria-rah
Not valid with hyphen, use cloneElement
Can't
Can fight the system and use cloneElement
Because modules are in another layer of language, cant simply assign in JSX.
Split into variable
let A = (~name) => <div> name </div>
<AlphaBet a=A />;
- Owner of
AlphaBet
in control, yay! - No pressure to prop spread in
A
(??? from wanting to be terse)
Don't Button && <Button />
Do showButton ? <Button /> : ReasonReact.nullElement
Mixins = no, Composing = yes
static
e.g. static defaultProps
, just use standalone value in module
instance
If doesn't refer to component (ReactJS this
), use normal value in module.
If it does, still try to turn into normal let value
that takes in an argument instead of reading the component's this
(???self)
- Look at terminal
<div onCLick={_e => self.send(Click)} />
- Error might be due to self not in scope
<div onClick={_e => self.ReasonReact.send(Click)} />
yay
info
Probably passed self.send
to helper function, that uses send ref twice
For complex reasons this doesn't type; you'd have to pass in the whole self to the helper.
element type is invalid... You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports
Probably:
- wrapping js comp w/
ReactReact.wrapJsForReason
- JS comp uses **
export default MyComp
(or missing) - using babel to compile es6 modules
So not:
[@bs.module] external myJSReactClass : ReasonReact.reactClass = "./myJSReactClass";
but:
[@bs.module "./myJSReactClass"] external myJSReactClass : ReasonReact.reactClass = "default";