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?
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 />)