Unbound record field id error
Asked Answered
R

1

6

I am trying out Reason-React. I am facing a problem when I try to add a key to one of the components.

I have a TodoApp that takes a list of TodoItem as state. The app works fine when I don't have a key for the TodoItem. When I add it, however I am getting a compilation error. I am adding the files here for reference:

TodoItem.re:

type item = {
  id: int,
  title: string,
  completed: bool
};

let lastId = ref(0);

let newItem = () => {
  lastId := lastId^ + 1;
  {id: lastId^, title: "Click a button", completed: false}
};

let toString = ReasonReact.stringToElement;

let component = ReasonReact.statelessComponent("TodoItem");

let make = (~item, children) => {
  ...component,
  render: (self) =>
    <div className="item">
      <input _type="checkbox" checked=(Js.Boolean.to_js_boolean(item.completed)) />
      (toString(item.title))
    </div>
};

TodoApp.re:

let toString = ReasonReact.stringToElement;

type state = {items: list(TodoItem.item)};

type action =
  | AddItem;

let component = ReasonReact.reducerComponent("TodoApp");

let currentItems = [TodoItem.{id: 0, title: "ToDo", completed: false}];

let make = (children) => {
  ...component,
  initialState: () => {items: currentItems},
  reducer: (action, {items}) =>
    switch action {
    | AddItem => ReasonReact.Update({items: [TodoItem.newItem(), ...items]})
    },
  render: ({state: {items}, reduce}) => {
    let numOfItems = List.length(items);
    <div className="app">
      <div className="title">
        (toString("What to do"))
        <button onClick=(reduce((_evt) => AddItem))> (toString("Add Something")) </button>
      </div>
      <div className="items">
        (
          ReasonReact.arrayToElement(
            Array.of_list(
              (List.map((item) => <TodoItem key=(string_of_int(item.id)) item />, items))
              /*List.map((item) => <TodoItem item />, items) This works but the above line of code with the key does not*/
            )
          )
        )
      </div>
      <div className="footer"> (toString(string_of_int(numOfItems) ++ " items")) </div>
    </div>
  }
};

I've added a comment near the line where the error occurs.

The error reads as Unbound record field id, but I am not able to figure out how it is not bound. What am I missing here?

Registry answered 15/11, 2017 at 6:28 Comment(0)
C
12

Type inference is unfortunately a bit limited when it comes to inferring the type of a record from another module based on the usage of record fields, so you need to give it some help. Two options that should work are:

Annotating the type of ìtem:

List.map((item: TodoItem.item) => <TodoItem key=(string_of_int(item.id)) item />)

or locally opening the module where the record field is used:

List.map((item) => <TodoItem key=(string_of_int(item.TodoItem.id)) item />)
Catching answered 15/11, 2017 at 6:33 Comment(4)
It's not really a type inference problem but a scoping problem (as the unbound value error seems to say). Record fileds are, as other named values qualified by the name of their defining module, and should therefore be accessed with the syntax : Module.name. The Open directive allows to omit the module name and the dot by extending the current scope with a module's named values. Here, i'm guessing that explicitly annotating the type of item extends locally the scope to add in it the TodoItem.item type.Bathy
@Bathy That does kind of make sense, but I find it odd (or at least out of the ordinary) that the scope would then be limited to a specific type.Catching
It worked, but I don't understand why. If its a scoping problem, how is it able to identify the type itself? Wouldn't the type itself be out of scope? Is it like the only the type's properties are out of scope? That seems weird as that means even though the type is inferred, we cannot access its properties unless explicitly stated?Registry
@ leoOrion the infered type is A record type, not THE TodoItem.item type. Because of the operator '.' which allows the access of a record field. The remaining question is which record type is it? It's the one that has a field named 'id', which should be in the scope. The only problem is that is not in scope.Bathy

© 2022 - 2024 — McMap. All rights reserved.