,
55 | nameS,
56 | );
57 | [@react.component]
58 | let make = () => componentFromSignal(vdomS, ());
59 | };
60 |
61 | ReactDOMRe.renderToElementWithId(, "index");
62 |
--------------------------------------------------------------------------------
/src/reactReact.re:
--------------------------------------------------------------------------------
1 | open ReactFrp.React;
2 |
3 | type signalPair('a) = {
4 | signal: signal('a),
5 | setSignal: 'a => unit,
6 | };
7 |
8 | let componentFromSignal = (propsToVdom, props) => {
9 | /* Starts with an empty element. That is, renders nothing */
10 | let (element, setElement) = React.useState(() => React.null);
11 |
12 | /* I'm now using useRef because it allows me to clean up resources */
13 | let propsPair = React.useRef(None);
14 | let watcher = React.useRef(None);
15 |
16 | React.useEffect0(() => {
17 | let (propsS, propsF) = S.create(props);
18 | propsPair.current = Some({signal: propsS, setSignal: x => propsF(x)});
19 | let vdomS = propsToVdom(propsS);
20 | /* I need to store the watcher in a variable, otherwise it can be gc'd
21 | and the signal(element) will stop updating the state */
22 | watcher.current =
23 | Some(S.map(newElement => setElement(_ => newElement), vdomS));
24 | Some(
25 | () => {
26 | /* Cleaning up resources */
27 | Belt.Option.map(propsPair.current, x =>
28 | S.stop(~strong=true, x.signal)
29 | )
30 | |> ignore;
31 | Belt.Option.map(watcher.current, S.stop(~strong=true)) |> ignore;
32 | propsPair.current = None;
33 | watcher.current = None;
34 | },
35 | );
36 | });
37 |
38 | React.useEffect1(() => {
39 | /* This is where the props passed by parameter become a signal */
40 | Belt.Option.map(propsPair.current, x => x.setSignal(props)) |> ignore;
41 | None
42 | }, [|props|]);
43 |
44 | element;
45 | };
46 |
47 | module Utils = {
48 | let valueFromEvent = ev => ReactEvent.Form.target(ev)##value;
49 | let emitEventToStream = (streamF, ev) => ev |> valueFromEvent |> streamF;
50 | let eventFromPromise = promise => {
51 | open Belt.Result;
52 | open Js.Promise;
53 | let (promiseE, promiseF) = E.create();
54 | promise
55 | |> then_(x => {
56 | promiseF(Ok(x));
57 | promise;
58 | })
59 | |> catch(x => {
60 | promiseF(Error(x));
61 | promise;
62 | })
63 | |> ignore;
64 | promiseE;
65 | };
66 | module Event = {
67 | let join = ee => E.switch_(E.never, ee);
68 | let bind = (e, f) => join(E.map(f, e));
69 | };
70 | };
71 |
--------------------------------------------------------------------------------
/src/reactReact.rei:
--------------------------------------------------------------------------------
1 | open ReactFrp.React;
2 |
3 | /*
4 | Generates a ReasonReact component from a ReactFrp signal.
5 |
6 | propsEq: overrides the equality function used to detect changes on
7 | the props. When a change is detected the props signal is updated
8 | accordingly.
9 |
10 | propsToVdom: a function that takes the props signal and returns a
11 | reactElement signal. The reactElement signal is the actual vdom you
12 | want to be rendered.
13 |
14 | props: the current value of the props (as received by make). It's
15 | usually a tuple, but you can create a custom type for it. The value
16 | will be fed to a signal.
17 | */
18 | let componentFromSignal:
19 | (signal('a) => signal(React.element), 'a) => React.element;
20 |
21 | module Utils: {
22 | /*
23 |
24 | Emits the value of a ReasonReact event into a signal or an event,
25 | returns a callback function to be used by the event prop of an
26 | element.
27 |
28 | For example, you can have a signal created with
29 |
30 | let (nameS, nameF) = S.create("");
31 |
32 | And in a text input you can have
33 |
34 |
35 |
36 | This way, whenever onChange happens, nameF is called and nameS is updated.
37 |
38 | You can do it for events in the same way.
39 |
40 | signalF: a function that emits a value to a signal or to an event
41 |
42 | It's called ToStream because it supports both signals and events
43 |
44 | */
45 | let emitEventToStream: ('a => unit, ReactEvent.Form.t) => unit;
46 | /*
47 |
48 | Converts a Js.Promise.t into an event.
49 |
50 | */
51 | let eventFromPromise:
52 | Js.Promise.t('a) => event(Belt.Result.t('a, Js.Promise.error));
53 | module Event: {
54 | /*
55 |
56 | Takes an event that produces other events and keeps it as a single
57 | stream of the latest event that was emitted.
58 |
59 | In other words, when a new event is emitted, the stream from
60 | the previous event is now ignored.
61 |
62 | */
63 | let join: event(event('a)) => event('a);
64 | /*
65 |
66 | Takes an event and a function that takes the value emitted by
67 | the first parameter to return an event, and then returns the
68 | event produced by this function.
69 |
70 | Similar to Js.Promise.then_
71 |
72 | */
73 | let bind: (event('a), 'a => event('b)) => event('b);
74 | };
75 | };
76 |
--------------------------------------------------------------------------------