Skip to content

Commit

Permalink
editing pass
Browse files Browse the repository at this point in the history
  • Loading branch information
jderochervlk committed Nov 26, 2024
1 parent 98b75ec commit 12193fc
Showing 1 changed file with 20 additions and 18 deletions.
38 changes: 20 additions & 18 deletions pages/docs/manual/latest/module-functions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,25 @@ canonical: "/docs/manual/latest/module-functions"
# Module Functions

Module functions can be used to create modules based on types, values, or functions from other modules.
This is a powerful tool that can be used to created abstractions and reusable code that might not be possible with functions, or might have a runtime cost if done with functions.
This is a powerful tool that can be used to create abstractions and reusable code that might not be possible with functions, or might have a runtime cost if done with functions.

This is an advanced part of ReScript and you can generally get by with normal values and functions.

## Quick example
Next.js has a `useNavigation` hook that returns an unknown type,
and it's up to the developer to add in a type annotation to define the type of the parameters returned by the hook.
Next.js has a `useParams` hook that returns an unknown type,
and it's up to the developer in TypeScript to add a type annotation for the parameters returned by the hook.
```TS
const params = useParams<{ tag: string; item: string }>()
```

Instead of having to add the type annotation every time you use the hook, you can create a module function that will return a typed response for the `useNavigation` hook.
In ReScript we can create a module function that will return a typed response for the `useParams` hook.
<CodeTab labels={["ReScript", "JS Output"]}>
```res example
module Next = {
// define our module function
module MakeNavigation = (Params: { type t }) => {
module MakeParams = (Params: { type t }) => {
@module("next/navigation")
external useNavigation: unit => Params.t = "useNavigation"
external useParams: unit => Params.t = "useParams"
/* You can use values from the function parameter, such as Params.t */
}
}
Expand All @@ -32,21 +34,21 @@ module Component: {
@react.component
let make: unit => Jsx.element
} = {
// Create a module that matches the module type expected by Next.MakeNavigation
module Params = {
// Create a module that matches the module type expected by Next.MakeParams
module P = {
type t = {
tag: string,
item: string,
}
}
// Create a new module using the Params module we created and the Next.MakeNavigation module function
module Navigation = Next.MakeNavigation(Params)
// Create a new module using the Params module we created and the Next.MakeParams module function
module Params = Next.MakeParams(P)
@react.component
let make = () => {
// Use the functions, values, or types created by the module function
let params = Navigation.useNavigation()
let params = Params.useParams()
<div>
<p>
{React.string("Tag: " ++ params.tag /* params is fully typed! */)}
Expand All @@ -62,16 +64,16 @@ module Component: {
import * as $$Navigation from "next/navigation";
import * as JsxRuntime from "react/jsx-runtime";

function MakeNavigation(Params) {
function MakeParams(Params) {
return {};
}

var Next = {
MakeNavigation: MakeNavigation
MakeParams: MakeParams
};

function Playground$Component(props) {
var params = $$Navigation.useNavigation();
var params = $$Navigation.useParams();
return JsxRuntime.jsxs("div", {
children: [
JsxRuntime.jsx("p", {
Expand Down Expand Up @@ -107,15 +109,15 @@ let getEnv = () => {
env
}
```
It's not possible to define types for this that will work for every project, so we just set it as 'a and the consumer of our library can define the return type when they use the function.
It's not possible to define types for this that will work for every project, so we just set it as 'a and the consumer of our library can define the return type.
```res
type t = {"LOG_LEVEL": string}
let values: t = getEnv()
```
This isn't great and it doesn't take advantage of ReScript's type system and ability to use types without type definitions, and it can't be easily shared across our application.

We can instead create a module function that can return a module that has contains a `getEnv` hook that has a typed response.
We can instead create a module function that can return a module that has contains a `getEnv` function that has a typed response.
```res
module MakeEnv = (
E: {
Expand All @@ -129,7 +131,7 @@ module MakeEnv = (
}
}
```
And now consumers of our library can define the types and create a custom version of the hook just for their application.
And now consumers of our library can define the types and create a custom version of the hook for their application.
Notice that in the JavaScript output that the `import.meta.env` is used directly and doesn't require any function calls or runtime overhead.

<CodeTab labels={["ReScript", "JS Output"]}>
Expand Down Expand Up @@ -249,7 +251,7 @@ function Person$1(props) {

## Dependency injection
Module functions can be used for dependency injection.
Here's an example of injecting in a some config values into a set of functions to access a database.
Here's an example of injecting in some config values into a set of functions to access a database.
<CodeTab labels={["ReScript", "JS Output"]}>
```res
module type DbConfig = {
Expand Down

0 comments on commit 12193fc

Please sign in to comment.