title | description | canonical |
---|---|---|
Arrays and Keys | Rendering arrays and handling keys in ReScript and React | /docs/react/latest/arrays-and-keys |
Whenever we are transforming data into an array of elements and put it in our React tree, we need to make sure to give every element an unique identifier to help React distinguish elements for each render. This page will explain the key
attribute and how to apply it whenever we need to map data to React.element
s.
Arrays require a special function React.array
to convert an array<Jsx.element>
to render as Jsx.element
.
<CodeTab labels={["ReScript", "JS Output"]}>
typetodo= {id: string, text: string} @react.componentletmake= () => { lettodos= [{id: "todo1", text: "Todo 1"}, {id: "todo2", text: "Todo 2"}] letitems=Array.map(todos, todo=> { <likey={todo.id}> {React.string(todo.text)} </li> }) <ul> {React.array(items)} </ul> }
functionPlayground(props){vartodos=[{id: "todo1",text: "Todo 1"},{id: "todo2",text: "Todo 2"}];varitems=todos.map(function(todo){returnJsxRuntime.jsx("li",{children: todo.text},todo.id);});returnJsxRuntime.jsx("ul",{children: items});}
Keys help React identify which elements have been changed, added, or removed throughout each render. Keys should be given to elements inside the array to give the elements a stable identity:
<CodeTab labels={["ReScript", "JS Output"]}>
letnumbers= [1, 2, 3, 4, 5] letitems=Array.map(numbers, (number) => { <likey={Int.toString(number)}> {React.int(number)} </li> })
varnumbers=[1,2,3,4,5];varitems=numbers.map(function(number){returnJsxRuntime.jsx("li",{children: number},number.toString());});
The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys:
<CodeTab labels={["ReScript", "JS Output"]}>
typetodo= {id: string, text: string} lettodos= [ {id: "todo1", text: "Todo 1"}, {id: "todo2", text: "Todo 2"} ] letitems=Array.map(todos, todo=> { <likey={todo.id}> {React.string(todo.text)} </li> })
vartodos=[{id: "todo1",text: "Todo 1"},{id: "todo2",text: "Todo 2"}];varitems=todos.map(function(todo){returnJsxRuntime.jsx("li",{children: todo.text},todo.id);});
If you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:
<CodeTab labels={["ReScript", "JS Output"]}>
letitems=Array.mapWithIndex(todos, (todo, i) => { // Only do this if items have no stable id <likey={Int.toString(i)}> {React.string(todo.text)} </li> })
varitems=todos.map(function(todo,i){returnJsxRuntime.jsx("li",{children: todo.text},i.toString());});
Keys used within arrays should be unique among their siblings. However they don’t need to be globally unique. We can use the same keys when we produce two different arrays:
<CodeTab labels={["ReScript", "JS Output"]}>
typepost= {id: string, title: string, content: string} moduleBlog= { @react.componentletmake= (~posts: array<post>) => { letsidebar= <ul> {Array.map(posts, post=> { <likey={post.id}> {React.string(post.title)} </li> })->React.array} </ul> letcontent=Array.map(posts, post=> { <divkey={post.id}> <h3> {React.string(post.title)} </h3> <p> {React.string(post.content)} </p> </div> }) <div> {sidebar} <hr /> {React.array(content)} </div> } } letposts= [ { id: "1", title: "Hello World", content: "Welcome to learning ReScript & React!", }, { id: "2", title: "Installation", content: "You can install reason-react from npm.", }, ] letblog= <Blogposts />
functionPlayground$Blog(props){varposts=props.posts;varsidebar=JsxRuntime.jsx("ul",{children: posts.map(function(post){returnJsxRuntime.jsx("li",{children: post.title},post.id);})});varcontent=posts.map(function(post){returnJsxRuntime.jsxs("div",{children: [JsxRuntime.jsx("h3",{children: post.title}),JsxRuntime.jsx("p",{children: post.content})]},post.id);});returnJsxRuntime.jsxs("div",{children: [sidebar,JsxRuntime.jsx("hr",{}),content]});}varBlog={make: Playground$Blog};varposts=[{id: "1",title: "Hello World",content: "Welcome to learning ReScript & React!"},{id: "2",title: "Installation",content: "You can install reason-react from npm."}];varblog=JsxRuntime.jsx(Playground$Blog,{posts: posts});
In case you ever want to render a list
of items, you can do something like this:
<CodeTab labels={["ReScript", "JS Output"]}>
typetodo= {id: string, text: string} @react.componentletmake= () => { lettodoList=list{ {id: "todo1", text: "Todo 1"}, {id: "todo2", text: "Todo 2"}, } letitems=todoList->List.toArray->Array.map(todo=> { <likey={todo.id}> {React.string(todo.text)} </li> }) <div> {React.array(items)} </div> }
functionPlayground(props){varitems=Core__List.toArray({hd: {id: "todo1",text: "Todo 1"},tl: {hd: {id: "todo2",text: "Todo 2"},tl: /* [] */0}}).map(function(todo){returnJsxRuntime.jsx("li",{children: todo.text},todo.id);});returnJsxRuntime.jsx("div",{children: items});}
We use List.toArray
to convert our list to an array before creating our array<React.element>
. Please note that using list
has performance impact due to extra conversion costs.
99% of the time you'll want to use arrays (seamless interop, faster JS code), but in some cases it might make sense to use a list
to leverage advanced pattern matching features etc.