Can Rescript handle partial application for React props?

Hi,
I wonder if it’s possible to partially apply React’s component props. It’s not critical to my work, but I’m just curious if it’s possible and if it’s not, why.

In the JS world, I can define something like this:

const test = (value) => ({ text }) => (
  <h3>
    {value} - {text}
  </h3>
);

And it works just fine.
I got in a situation where it could be handy, but because the RescriptReact component is a module and requires @react.comopnent macro or @obj external makeProps to define such a function.
I prepared an example that doesn’t compile:

module Test = {
  module Base = {
    @react.component
    let make = (~x: int, ~y: int) => <p> {string_of_int(x + y)->React.string} </p>
  }

  module Partial = {
    @react.component
    let make = Base.make(~x=2)
  }
}

Of course, I could do just let make = (~y) => <Base x y /> but for the sake of solving the riddle, I wonder if Rescript type system allows what I tried to do (but maybe in a different fashion).

havn’t looked but suspect not… since the arguments on make desugar into an Object and then that is passed as props.

maybe something like this using functors?

module type Config = {
  let value: int
}

module MakeX = (Config: Config) => {
  @react.component
  let make = (~text) => {
    <h3> {React.int(Config.value)} {React.string(" ==> ")} {React.string(text)} </h3>
  }
}

@react.component
let make = () => {
  let (count, setCount) = React.useState(() => 0)

  module Actual = MakeX({
    let value = count
  })

  module Opposite = MakeX({
    let value = -count
  })

  <>
    <button onClick={_ => setCount(c => c + 1)}> {React.string("Inc")} </button>
    <button onClick={_ => setCount(c => c - 1)}> {React.string("Dec")} </button>
    <Actual text="Actual" />
    <Opposite text="Opposite" />
  </>
}


1 Like

I guess it’s hard to do it with the @react.component annotation because we have no control over the prop type. Without the sugar, something like this might work:

module Button = {
  @react.component
  let make = (~count: int, ~name: string) => {
    let times = switch count {
    | 1 => "once"
    | 2 => "twice"
    | n => Belt.Int.toString(n) ++ " times"
    }
    let msg = name ++ " clickd me " ++ times

    <button> {msg->React.string} </button>
  }
}

module Partial = {
  let makeProps = Button.makeProps(~count=10)

  let make = props => {
    let name = props["name"]
    let count = props["count"]

    let msg = name ++ " clickd me " ++ Belt.Int.toString(count)

    <button> {msg->React.string} </button>
  }
}

let a = <Partial name="test" />

Playground Link

At this point, I’d consider a wrapper function to be the cleaner solution though.

1 Like

Thanks!
I like both approaches so I don’t know which one I should pick as the solution :sweat_smile:
Functor seems to be a bit verbose. On the other hand, using strings for object keys always makes me nervous, as “magic strings” are always easy to misspell.

Originally I wanted to build a base wrapper for the Fontawesome library and create “child” components for every icon I would use. Still, I found out that even a wrapper function would be too much boilerplate than just passing props in all places :smile:

Object keys may be mispelled, but the compiler would not compile in this case.

Why don’t you write a little JS script that uses a template string with the boilerplate code and generate a .res file out of that? You could probably even access all the possible icons from the library to help you with the automation.