Declaring empty object with record mode


#1

Switching over from Js.t to bs.deriving has been simple for the most part, but I have hit a wall with declaring a empty object in record mode. I have tried Js.Obj.empty() and Js.Dict.empty(). I’ve duplicated the issue below, the problem I am having is in the initialState:

[@bs.deriving abstract]
type person = {
  name: string,
  age: int
};

let person_1 = person(~name="Joe", ~age=31);

type state = {
  person
};

type action =
  | FetchPerson(person);

let component = ReasonReact.reducerComponent("Person");
let make = _children => {
  ...component,
  initialState: () => {
    person: Js.Obj.empty()
  },
  reducer: (action, _state) =>
    switch (action) {
    | FetchPerson(data) => ReasonReact.Update({person: data})
  },
  render: self => {
    <div>
      <button onClick=((_) => self.send(FetchPerson(person_1)))>
        (ReasonReact.string("load person info"))
      </button>
      <div>(ReasonReact.string(self.state.person |. name))</div>
    </div>
  }
};

If I switch over to using Js.t syntax, code compiles and works. Is empty object not supported yet in bs.deriving record mode?


#2

Since it’s an abstract type it’s not a Js object in reason land anymore so my first thought would be to make the person in your state an option with something like:

...

type state = {
  person: option(person)
};

type action =
  | FetchPerson(person);

let component = ReasonReact.reducerComponent("Person");
let make = _children => {
  ...component,
  initialState: () => {
    person: None
  },
  reducer: (action, _state) =>
    switch (action) {
    | FetchPerson(data) => ReasonReact.Update({person: Some(data)})
  },
  render: self => {
    /* handle showing the person info or a loading message depending on the person option */
  }
};

If you really do need to make an empty object then you’ll need to make all your props optional in your type

[@bs.deriving abstract]
type person = {
  [@bs.optional] name: string,
  [@bs.optional] age: int
};

let person_1 = person(~name="Joe", ~age=31, ());

...

let make = _children => {
  ...component,
  initialState: () => {
    person: person(()) /* this will make an empty object in your bs.js output*/
  },
  reducer: (action, _state) =>
    switch (action) {
    | FetchPerson(data) => ReasonReact.Update({person: data})
  },
  render: self => {
    /* you'll still need to handle unwrapping the option for each property before accessing it */
  }
};

#3

whoa! super cool! this helps a ton! thanks @carbcola!