React.createContext, default values

I would like to define default values for an object passed to the React.createContext function:

// TreeContext.res
let context = React.createContext({
  "selectedId": "", // I would like this to be optional
  "theme": "dark"
})

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

Of course, the following code:

// Tree.res
@react.component
let make = () =>{
  <TreeContext value={ "theme": "light" }>
    {React.string("Hi there!")}
  </TreeContext>
}

throws an error:

This has type: {"theme": string}
Somewhere wanted: {"selectedId": string}
The second object type has no method theme

Would defining a type for your context work, where selectedId is an option<string>?

For example:

TreeContext.res

type t = {
  selected: option<string>,
  theme: string,
}

let context = React.createContext({
  selected: None,
  theme: "dark",
})

let makeProps = (~value, ~children, ()) =>
  {
    "value": value,
    "children": children,
  }

let make = React.Context.provider(context)

Tree.res

@react.component
let make = () => {
  let value: TreeContext.t = {selected: None, theme: "light"}
  <TreeContext value> {React.string("Hi there!")} </TreeContext>
}

What do you think?

Edit: And for some further type benefits, you could type the theme as well:

type t = {
  selected: option<string>,
  theme: [#dark | #light],
}

Thanks, kevanstannard. I would like to go even further and omit the selected attribute. For example:

@react.component
let make = () => {
  let value: TreeContext.t = {theme: "light"}
  <TreeContext value> {React.string("Hi there!")} </TreeContext>
}

To summarize:

// TreeContext.res
let context = React.createContext({
  "attr1": Some("Value 1"), // I want this attr to be optional
  "attr2": "Value 2"
})

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

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

And I’d like to omit the attr1 attribute:

// Tree.res
@react.component
let make = () =>{
  // I would like to omit "attr1", but Rescript forces me to write None
  <TreeContext.Provider value={ "attr1": None, "attr2": "Some value" }>
    {React.string("Hi there!")}
  </TreeContext.Provider>
}

You’ll probably need to create a @obj based external for creating your JS object with a variable set of attributes (in this case, omit the attr1 value).

type context

@obj
external contextValue: (~attr1: string=?, ~attr2: string, unit) => context = ""

// Will create your `value` without an `attr1`
let context = React.createContext(contextValue(~attr2="Value2", ()))

Full example with your Provider: Playground Link

Instead of @obj, you could also use @deriving(abstract). I think the obj version is leaner and more direct.

2 Likes