Possible to use React Context API across JS and Reason boundaries?


#1

I know this will be addressed in future versions of ReasonReact, however I tried to write bindings to the context API in React like this:

module CreateContext = (ContextConfig: {type state; let defaultValue: state;}) => {
  include ContextConfig;

  [@bs.deriving abstract]
  type context = {
    [@bs.as "Provider"]
    providerJs: ReasonReact.reactClass,
    [@bs.as "Consumer"]
    consumerJs: ReasonReact.reactClass,
  };

  [@bs.module "react"]
  external createContext : state => context = "createContext";

  let js = createContext(defaultValue);

  module Provider = {
    [@bs.deriving abstract]
    type jsProps = {value: state};
    let make = (~value, children) =>
      ReasonReact.wrapJsForReason(
        ~reactClass=js |. providerJs,
        ~props=jsProps(~value),
        children,
      );
  };

  module Consumer = {
    let make = (children: state => 'a) =>
      ReasonReact.wrapJsForReason(
        ~reactClass=js |. providerJs,
        ~props=children,
        children,
      );
  };
};

Then I wrap it in my own Reason React component:

module Provider = {
  let component = ReasonReact.reducerComponent("StoreProvider");

  let make = children => {
    ...component,
    initialState: () => HaveNotAsked,
    didMount: initialize,
    reducer: (action, _state) =>
      switch (action) {
      | UpdateState(newState) => ReasonReact.Update(newState)
      },
    render: self =>
      switch (self.state) {
      | HaveNotAsked => ReasonReact.null
      | Requested => ReasonReact.null
      | Error(reason) => ReasonReact.string("ERROR" ++ reason)
      | Loaded(state) =>
        Js.log2("IMA RENDER THIS CHILDREN", state);
        <Context.Provider value=state> ...children </Context.Provider>;
      },
  };
};

then I write JS bindings to it (so I can use it in the legacy part of the code)

let consumerJs = Context.(js |. consumerJs);

let providerJs =
  ReasonReact.wrapReasonForJs(~component=Provider.component, jsProps =>
    Provider.make(jsProps##children)
  );

However when I use it in my JavaScript side of things always falls back to the default value which from what I understand is an indication that it can’t find the provider even though it’s defiantly higher in the component tree:

Is this expected? Is there something about wrapping JS components that doesn’t presreve the context?


#2

There is a pure Reason implementation of it!
It’s simpler than the JS counterpart but it is functional. Tho I don’t use it a lot.


#3

This is what I started with originally, however I think it might also struggle when switching between JS and Reason.


#4

I’ve also used the reason-react-context with a lot of success but i’ve only used in in Reason code, not JS. Please let us know if you figure out anything further with your context bindings :+1: