reason-react icon indicating copy to clipboard operation
reason-react copied to clipboard

Cannot use memoCustomCompareProps

Open benadamstyles opened this issue 6 years ago • 6 comments

Attempting to use React.memoCustomCompareProps throws the following compiler error:

Invalid_argument("react.component calls can only be on function definitions or component wrappers (forwardRef, memo).")

benadamstyles avatar May 05 '19 12:05 benadamstyles

The same is true of React.Context.provider(context)

benadamstyles avatar May 05 '19 12:05 benadamstyles

Can you give an example of how context was giving you trouble? It can work fine if you drop out of JSX with something like


let context = React.createContext(defaultContextValue);
React.Context.provider(context);

[@react.component]
let make = (~val, ~children) => {
  React.createElement(
    provider,
    {"value": ~val, "children": children},
  );
};

Or if you copy over the make + makeProps directly from provider.

Looking at memoCustomCompareProps!

rickyvetter avatar May 11 '19 05:05 rickyvetter

@rickyvetter do you know if the current context API is final? It is a bit cumbersome and not obvious right now. Since it is not documented yet and was not mentioned in the release notes I guess it is still a work in process.

These two options work right now:

module Provider = {
  let provider = React.Context.provider(context);

  [@react.component]
  let make = (~value, ~children) => {
    React.createElement(provider, {"value": value, "children": children});
  };
};

and

module Provider = {
  let makeProps = (~value, ~children, ()) => {
    "value": value,
    "children": children,
  };
  let make = React.Context.provider(context);
};

but thats kind of "manual", I was expecting something similar to:

module Provider =
  React.Context.Provider.Make({
    type t = int;
    let context = React.createContext(0);
  });

implemented like:

module type S = {
  type t;
  let context: React.Context.t(t);
};

module Make = (C: S) => {
  let makeProps = (~value, ~children, ()) => {
    "value": value,
    "children": children,
  };
  let make = React.Context.provider(context);
};

or maybe for the whole context bundle to be created with a functor (resulting in a module with Consumer/Provider submodules)

tizoc avatar Jun 19 '19 17:06 tizoc

Functor bundling everything:

module type CONFIG = {
  type t;
  let initialValue: t;
};

module Make = (Config: CONFIG) => {
  type t = Config.t;

  let context = React.createContext(Config.initialValue);
  let use = () => React.useContext(context);

  module Provider = {
    [@react.component]
    let make = {
      let provider = React.Context.provider(context);

      (~value=Config.initialValue, ~children) => {
        React.createElement(
          provider,
          {"value": value, "children": children},
        );
      };
    };
  };

  module Consumer = {
    [@react.component]
    let make = (~children) => <> {children(use())} </>;
  };
};

Example

module MyContext =
  Context.Make({
    type t = string;
    let initialValue = "hello";
  });

then

<MyContext.Provider value="Bye">
  <MyContext.Consumer>
    {value => s(value)}
  </MyContext.Consumer>
</MyContext.Provider>

and also

let myContext = MyContext.use();
// or
let myContext = React.useContext(MyContext.context);

tizoc avatar Jun 19 '19 18:06 tizoc

I've found a workaround for this, which is so simple that it suggests changing this in reason-react itself might be a single line change somewhere (will see if I can PR this separately):

let memo = React.memoCustomCompareProps(_, (_, _) => true);

[@react.component]
let make = memo(() => <View />);

EDIT: I've had a look at the ppx code and I'm afraid it's beyond me!

benadamstyles avatar Apr 19 '20 13:04 benadamstyles

Would be good to find a fix for this!

jfrolich avatar Aug 30 '21 02:08 jfrolich