scala.js

Is there a useState concept so I can create a service which holds data that is accessed in multiple components?


I have 2 components who want to access the same data. Instead of each doing an HTTP Request independantly, I wanted to keep the items in parity. When doing react, we can easily do: const [ data, setData ] = useState(undefined) which will allow us to use data in our app and setData to change the global.

I was trying to think of how this might be doable in ReactScala, and Was thinking that there could be some overlap here since you can do something like:

useState[A]( data: A ): Pair[A, A=>A] = {
   val d = data
   return d, x => {
     d = x
     return d
   }
}

or similar.

I have not seen the documentation on useState in Japgolly as much as defining the property in the component state and then using the state.copy() function to update the value.

The issue which occurred is that to me, state.copy is just 1 component, and wanted to know if there was a way to genericize.


Solution

  • https://github.com/japgolly/scalajs-react/blob/master/doc/HOOKS.md

    Under the HOOKS file linked above, the top example shows how useState is translated. I will add it below in case the file is changed or deleted:

    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      });
    
      const [fruit, setFruit] = useState("banana");
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
          <p>Your favourite fruit is a {fruit}!</p>
        </div>
      );
    }
    

    Compared to:

    import japgolly.scalajs.react._
    import japgolly.scalajs.react.vdom.html_<^._
    import org.scalajs.dom.document
    
    object Example {
      val Component = ScalaFnComponent.withHooks[Unit]
    
        .useState(0)
    
        .useEffectBy((props, count) => Callback {
          document.title = s"You clicked ${count.value} times"
        })
    
        .useState("banana")
    
        .render((props, count, fruit) =>
          <.div(
            <.p(s"You clicked ${count.value} times"),
            <.button(
              ^.onClick --> count.modState(_ + 1),
              "Click me"
            ),
            <.p(s"Your favourite fruit is a ${fruit.value}!")
          )
        )
    }