1

I have been using React for a long time and I would like to give a chance to Reason React. I created then a small project to fetch data from a Github repository and then display results.

I would like to use React Hooks and store results into my state.

Here's what I'm trying to do:

type githubRepo = {
  name: string,
  url: string,
};

module Decode = {
  let repo = (repo): githubRepo =>
    Json.Decode.{
      name: repo |> field("name", string),
      url: repo |> field("url", string),
    };
};

let useFetch = (~url: string, ~options=?, ()) => {
  let (loading, setLoading) = React.useState(() => true);
  let (result, setResult) = React.useState(() => []);

  React.useEffect1(
    () => {
      ignore(
        Js.Promise.(
          Fetch.fetch(url)
          |> then_(Fetch.Response.json)
          |> then_(json =>
               json
               |> Decode.repo
               |> (
                 decodedRepo => {
                   Js.log(decodedRepo);
                   setResult(result => decodedRepo);
                   setLoading(_ => false);
                   Some(decodedRepo);
                 }
               )
               |> resolve
             )
          |> catch(_err => resolve(None))
        ),
      );

      None;
    },
    [||],
  );

  (loading, result);
};

When I try to do setResult(_ => decodedRepo) it throws an error

This has type: githubRepo But somewhere wanted: list('a)

I know I've initialized my React.useState to an empty list but I can't figure out how to set my decodedRepo inside.

Here's the result of Js.log(decodedRepo):

Array [ "Node.js", "https://api.github.com/orgs/nodejs" ]
  0: "Node.js"
  1: "https://api.github.com/orgs/nodejs"
  length: 2

I would also like to know if there is a way to init useState with no value?

glennsl
  • 28,186
  • 12
  • 57
  • 75
Hurobaki
  • 3,728
  • 6
  • 24
  • 41
  • Do you want `result` to be a list of `githubRepo`s, or do you just not know how to initialize it to something "empty" other than a list? – glennsl Jun 24 '19 at 13:07
  • Hi, actually I would like to know both cases. Because I think right now I just want to have a `githubRepo` in my state but I also want to know how to properly store a list of `githubRepo` – Hurobaki Jun 24 '19 at 13:39

1 Answers1

3

You can prepend an item to a list by using the list spread syntax, similarly to how you would in JavaScript:

setResult(result => [decodedRepo, ...result]);

But if you don't want to deal with the possibility of more than one item, you need to use a container that is restricted to either zero or one item only. This is the option type. In use it would look something like this:

let (result, setResult) = React.useState(() => None);
setResult(result => Some(decodedRepo));

Then when you use result you are forced to deal with the possibility that it may or may not contain an item:

switch (result) {
| Some(decodedRepo) => ...
| None => ...
}
glennsl
  • 28,186
  • 12
  • 57
  • 75
  • Thank you it works as expected ! :D I have one last question how to exploit my Array ? It seems I can't do anything with it ... I can open a new SO post if you want, thank you in advance :) – Hurobaki Jun 24 '19 at 23:16
  • Exploit? Yeah a new post would probably be good to explain what you want to do and what doesn't work as you expect. – glennsl Jun 24 '19 at 23:27
  • Actually it works as I asked thanks to you. When I `Js.log(decodedRepo)` I've got this : `Array[ "Node.js", "https://api.github.com/orgs/nodejs"]` can't figure out how to retrieve these value aka "Node.js" and "https://api.github.com/orgs/nodejs". – Hurobaki Jun 24 '19 at 23:31
  • It's not an array in Reason, it's just that records are represented as arrays in JavaScript and `Js.log` prints the JavaScript representation, not the Reason representation. You can access the fields with `decodedRepo.name` and `decodedRepo.url`. – glennsl Jun 24 '19 at 23:34
  • 1
    It works ! Thank you man ! My JS approach duped me ... Thank you for your lib also :) – Hurobaki Jun 24 '19 at 23:38