Search code examples
reactjsfluxscala.js

Using scalajs Diode and an ApplicationCircuit with a handler defined for a Posts (sequence), can I somehow map it to a ModelProxy[Post] individual?


Edit 2:

There has to be a better way than what I am doing in my haribranedness

        <.div(
          ApplicationCircuit.zoom(_.posts.postList).value.map {
            case p: Post ⇒
              ApplicationCircuit.connect(_.posts.postList.filterNot(x ⇒ x == p).head)(x ⇒ PostItemC(PostItemC.Props(x)))
          }
        )

It's a weird question but I have my reasons. When I render each Post from ModelProxy[Posts] where case class Posts(seq: Seq[Post]), I'd like to instead render a ModelProxy[Post], a proxy for each one. This will allow me to, moving forward, wrap it in a Pot and then handle individual Post updates, deletes, etc with great ease.

I'm having trouble finagling the object into that form, though, but I feel like there are so many zoomFlatMap and zoomFlatMapRW etc that something should help me get from where I am to there. That said, I am lost.

Edit

My closest attempt

    <.div(
      ApplicationCircuit.zoom(_.posts.postList).value.map {
        case p: Post ⇒
          ApplicationCircuit.connect(_.posts.postList.filterNot(x ⇒ x == p).head)(_.)
      }

ie, ApplicationCircuit.connect(_.posts.postList)(proxy ⇒ ModelProxy(????))

object ApplicationCircuit
  extends Circuit[ApplicationModel]
  with ReactConnector[ApplicationModel] {

  addProcessor(new DiodeLogger[ApplicationModel]())

  override protected def initialModel: ApplicationModel = ApplicationModel(
    Posts(Seq()),
    Masthead(NavigationItems(Seq()), "JustinTampa", "JustinTampa.com", active = false)
  )

  override protected def actionHandler = composeHandlers(
    new PostHandler(zoomRW(_.posts)((m,v) ⇒ m.copy(posts = v))),
    new MastheadHandler(zoomRW(_.masthead)((m,v) ⇒ m.copy(masthead = v)))
  )

Solution

  • In your render code you should really only use functions from the ReactConnector trait, like wrap and connect. For this case it's probably best to connect to Posts and then render individual items in the sequence.

    ApplicationCircuit.connect(_.posts.postList){ model =>
      <.div(model().map(post => PostItemC(PostItemC.Props(post))))
    }
    

    Then in your PostItemC component you should implement the shouldComponentUpdate method, so that posts that have not changed are not rendered again when the postList changes. Something like,

    shouldComponentUpdate(scope => scope.currentProps.post ne scope.nextProps.post)
    

    Finally make sure your component defines the key property so that React will know how to connect the post components when they are updated. Otherwise you will get a runtime warning about not having key in an array of components.

    This way when the model changes (generating a new postList) React will render the list again, but will skip rendering items that have not changed.

    In contrast if you only connect individual posts, then changes to the list itself (like adding or removing posts) do not trigger rendering.