I'm learning reason react and am having trouble rendering a list of items I'm fetching from a json api.
let url = region => {
"https://api.openbrewerydb.org/breweries?by_state="
++ Js.String.replaceByRe([%re "/\\s/g"], "_", String.lowercase(region));
};
[@react.component]
let make = (~region) => {
let (breweries, setBreweries) = React.useState(() => []);
React.useEffect1(
() => {
Js.Promise.(
Axios.get(url(region))
|> then_(response => {
resolve(setBreweries(response##data));
})
);
None;
},
[|region|],
);
let renderBrewery = brw => <div> {ReasonReact.string(brw##name)} </div>;
<div>
{breweries |> List.map(renderBrewery) |> Array.of_list |> React.array}
</div>;
};
I am getting back the data I expect in the then_
block. However, when the items are rendered I get a JS error saying TypeError: brw is undefined
meaning that somewhere there is a discrepancy between the object I receive and the object I'm trying to render.
The problem is (almost certainly) that you're using List.map
on an array.
Lists and arrays are not the same. The array [|1, 2, 3|]
will be represented as an ordinary array in JavaScript, but a list [1, 2, 3]
will be represented as nested two-element arrays, [1, [2, [3, 0]]]
, with each array (cons-cell) containing one element and a pointer to the next cell, or 0
as a terminator.
List.map
will continue to iterate until it encounters 0
as the second element of a cell, which of course it never does. Instead it continues to iterate on garbage data until it crashes.
The solution should be as simple as replacing List.map
with Array.map
and removing Array.of_list
since that's now unnecessary.
It's also worth noting that this might have been prevented with type annotations, and almost certainly with JSON decoders. Having to spend time tracking down hard to catch errors like this is the price you pay for the convenience of dynamic typing.