A few small questions

(No intention to spam, just questions popping up which I want to understand better. This thread is piling up nice answers so maybe I can redact it into a FAQ later on.)

How do I handle simple but oft-occurring cases with undefined involved?

In JS:

...

const { something } = useRouter()

if (!something) {
  // not loaded yet
  return </>
}

This is trivial to handle in JS. Since useRouter may return undefined, something will be destructured into undefined.

How do I do this in neatly ReScript? Right now, I have to use optionals and it becomes really messy.

Some good general advice is to bind only what you need, when you need it.

For JS function calls that introspect the number of arguments to determine what type a positional argument is, I would always reach for a new external binding to the same function as described in the docs here

If there’s only one last optional positional argument, you can also do something like this:

@send
external func: (t, ~options: options=?, unit) => unit = "func"

I love that you’re asking so many questions here, it’s going to help a lot of newcomers in the future. I’m sure it’s already been useful to some.

After using ReScript for a while, I’ve found this to be more useful than annoying. It prevents any kind of runtime issues with rendering undefined or something that isn’t valid jsx.

That said, it’s been very common in my codebase to do something like this:

<>
  {switch value {
  | Some(str) => React.string(str)
  | _ => React.null
  }}
</>

Or:

<>{value->Option.mapWithDefault(React.null, React.string)}</>

This is incredibly useful for apis like @apollo/client or react-query:

switch result {
| {loading: true, _} => <Spinner />
| {error: Some(message), _} => <Error message />
| {data: Some(data), _} => </>
| _ => <Error message="something went wrong" />
}

It would be nice to have React.option: option<React.element> => React.element to pipe:

<>{value->Option.map(React.string)->React.option}</>

or even: React.optString: option<string> => React.element etc

2 Likes

Thank you.

Should I generally avoid opening modules?

I’m discovering that some names are common and the modules start shadowing each other. How do you deal with this? Almost any module I open, I get a type named t in my scope, which can surely cause confusion. open Promise seems like a sane thing to do, but I don’t want to import type tsilently.

I usually don’t open any modules besides Belt and our UI library.

2 Likes

Avoid opening modules at the top of the file where they can pollute the global scope of the module and confuse code readers. Try to open modules in smaller, local scopes. Only a few modules, like Belt, are explicitly designed to be opened globally, and even they can confuse people if they don’t read the documentation and understand the implications.

1 Like

You can open modules inside of functions, I find this pretty helpful for improving readability in small sections.

let render = (definition, button) => {
   // (...)

   open Message.Embed
   let embed = Message.Embed.make()
      -> setColor (16201999)
      -> setAuthor (definition.word, icon)
      -> addField ("Definition", definitionText, Full)
      -> addField ("Examples", exampleText, Full)
      -> setFooter ("Definition was pulled from urbandictionary.com")

   Message.EmbedWithComponents (embed, [actions], Public)
}

(Not REALLY a question, but kind of a request)

Where are the conventions for ReScript?

I think we need a style guide. Maybe it’s just me - I don’t know - but I have a much easier time getting into something after ready a style guide. It not only gives me a quick direction for laying out code, but also a sort of abstract overview of the language and the thinking that goes with it.

Especially considering how strict the autoformatter is, I think there should be a guide outlining naming conventions and such.

E.g., Namespace_Module for namespacing. Do we prefer #some_variant, #someVariant, #SomeVariant or #SOME_VARIANT - and why?

Yes, you could say “hey, this is up to you, who cares - do what you like in your projects,” but the same could be said of formatting entirely. For the sake of giving newbies even more ground to stand on, and maybe even more importantly, for us as a community to think about how we use the language, a style guide would be really helpful.

What are your pet peeves when it comes to abusing ReScript? What do you hate to see and why?

3 Likes

Or even better to wrap it in a block:

let render = (definition, button) => {
   // (...)

   
   let embed = {
     open Message.Embed
     make()
      -> setColor(16201999)
      -> setAuthor(definition.word, icon)
      -> addField("Definition", definitionText, Full)
      -> addField("Examples", exampleText, Full)
      -> setFooter("Definition was pulled from urbandictionary.com")
   }

   Message.EmbedWithComponents (embed, [actions], Public)
}
2 Likes

How can I store a reference to a component (or more generally, module?)

What I’m trying to accomplish:

open MyIconLib

let myIcon = isFancy ? MyFancyIcon : MyRegularIcon
<myIcon />
let myIcon = isFancy ? <MyFancyIcon/> : <MyRegularIcon/>

<div>
  {myIcon}
</div>

Yes, but the problem here is that I have to render the comopnent to do that. That means I can’t or shouldn’t do it outside a component render function, right?

How do I structure files for Next?

As you know, Next uses filenames for routing. I think earlier in this thread, someone suggested I put .js-files to import the res-components. This was fine when I had 5-7 pages, but now that the project is 30-40 pages with 20 api points, the ReScript source folder is becoming crowded:

image

And so on, which are in turned imported from a bunch of js files.

How do you guys deal with this?

Looks like you want to do something similar to what described in the article: Cool Things You Can Do with First-Class Modules in ReScriptReact

We have separate directories for pages and components. Although they are not needed in ReScript, but help to group common things.

Are you sure you need them to be components? Are they stateful? I just have icons as ordinary values in an Icon module:

let myIcon = isFancy ? Icon.fancy : Icon.regular

Deal with what exactly? Having a directory with lots of files isn’t a problem in itself I don’t think.

IMO, wrapping the component in a function is easier than dealing with first-class modules:

let myIcon = (isFancy, a, b) =>
  isFancy
    ? <MyFancyIcon prop1=a prop2=b />
    : <MyRegularIcon prop1=a prop2=b />

<div>
  {myIcon(isFancy, a, b}
</div>
1 Like

What is the importance of .resi files?

I understand they define the interface. Why would I want to put it separately? It reminds me of .h and .c files. Is that a good way of thinking about .resi files/

What are some tips to become more productive with ReScript?

Do you have any major or minor workflow tips to increaes productivity? Right now, I’m spending a lot of time on bindings. A large part of an app is interacting with third party libs and APIs. If I don’t make them typesafe, type unsafety creeps into my entire app, so that is taking a lot of time.