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?
1 Answer
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 />) 4