Code Monkey home page Code Monkey logo

diode's People

Contributors

a1kemist avatar cquiroz avatar dependabot[bot] avatar dylanarnold avatar fdietze avatar glmars avatar harana-bot avatar helllamer avatar jluhrs avatar jonas avatar michalzdzinski avatar mla310 avatar mprevel avatar netzwerg avatar nightkr avatar ochrons avatar perwiklander avatar povder avatar ramnivas avatar refond avatar registrs avatar russwyte avatar scala-steward avatar sidnt avatar taig avatar thsoft avatar toddburnside avatar torstenrudolf avatar vergenzt avatar vpavkin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

diode's Issues

"inferred existential type" warning when putting ReactConnectorProxy in State

I'm following the example in the Usage with React docs that suggest storing the ReactConnectorProxy in the State (in the example it has case class State(component: ReactConnectProxy[Pot[String])).

When doing that I am getting an "inferred existential type" warning. For example, doing

case class State(component: ReactConnectorProxy[Model])

results in

inferred existential type Option[japgolly.scalajs.react.ReactComponentC.ReqProps[diode.react.ModelProxy[Model] => japgolly.scalajs.react.ReactElement,Model,_$1,org.scalajs.dom.raw.Element]] forSome { type _$1 }, which cannot be expressed by wildcards,  should be enabled
by making the implicit value scala.language.existentials visible.
This can be achieved by adding the import clause 'import scala.language.existentials'
or by setting the compiler option -language:existentials.
See the Scaladoc for value scala.language.existentials for a discussion
why the feature should be explicitly enabled.

Better name for model in Circuit object

In this code

object AppCircuit extends Circuit[RootModel] {
  var model = RootModel(0)
  val actionHandler: PartialFunction[AnyRef, ActionResult[RootModel]] = {
    case Increase(a) => ModelUpdate(model.copy(counter = model.counter + a))
    case Decrease(a) => ModelUpdate(model.copy(counter = model.counter - a))
    case Reset => ModelUpdate(model.copy(counter = 0))
  }

I think the model variable should be rename initialState, and changed to a value (val) instead. There should be a private model variable (var model) in the Circuit object that subsequence inheritant cannot access and change. This is to avoid misunderstanding that model is something that can be modify if see fit.

scala-js react 2

Hello,

are there any plans of migrating this to scalajs-react 2.0?

Thanks :)

Rendering not triggered by dispatching actions after mounting a component

I'm using the todomvc example to explain my problem:
Suppose you add a new action "InitTodos" and therefore you add a new case in the handler:

case InitTodos => 
      val todos = List(Todo(TodoId.random, "first", false), Todo(TodoId.random, "second", false))
      updated(todos)

and then consider that the TodoList loads a initial set of todos by dispatching a InitTodos action after mounting the component:

...
class Backend($: BackendScope[Props, State]) {
    def mounted(props: Props) = {
      println("initializing todos")
      props.proxy.dispatch(InitTodos)
    }
  ....
}
...
private val component = ReactComponentB[Props]("TodoList")
    ....
    .componentDidMount(scope => scope.backend.mounted(scope.props))
    .build

I would expect the list to be populated but instead I only observe the message in the console "initializing todos". If I dispatch the same action but manually, say for example, from a button, then the list is updated as expected.

Is this ok? Or maybe there is something wrong with what I'm doing?

Thanks! and sorry for my english.

foldHandlers not working in 0.5.2

First of all, congratulations to such a thoughtful library!
I have experienced in my usage just a small glitch so far. The same action is only handled by one of the handlers. Before slimming down my example, I noticed that CircuitTests (see link below) is comparing (wrongly) against the initial value 42, whereas it should compare to 43. I guess that's the reason why the test case doesn't fail and the bug is not showing up.
https://github.com/ochrons/diode/blob/master/diode-core/shared/src/test/scala/diode/CircuitTests.scala#L363

ActionBatch fails to execute when handler defined

The issue revolves around this code:
https://github.com/suzaku-io/diode/blob/master/diode-core/shared/src/main/scala/diode/Circuit.scala#L298

If a handler is defined than baseHandler is never called which means that ActionBatch actions are never executed.
It seems like the code should be reversed where you check if it's an ActionBatch first and then default to the provided handler.

And if you try and manually handle the ActionBatch yourself in your own handler it won't work because the ActionBatch class is not a case class:
https://github.com/suzaku-io/diode/blob/master/diode-core/shared/src/main/scala/diode/Circuit.scala#L46

Make fetch async

I sometimes get this message in the console when using PotMap:

Warning: DiodeWrapper is accessing isMounted inside its render() function.
render() should be a pure function of props and state. It should never
access something that requires stale data from the previous render,
such as refs. Move this logic to componentDidMount and componentDidUpdate instead.

It looks like the problem is that the mapHandler in AsyncActionRetriable wants to set the value of the Pot to Pending right away. If this happens while rendering, React gets cranky.

Reusing dispatching functions with React

Is there any way to use reusable functions for dispatch calls, to avoid repeated rendering of buttons etc.? (https://github.com/japgolly/scalajs-react/blob/master/doc/PERFORMANCE.md)

Currently I've defined a singleton for the action functions, and passing that object for every component that needs to dispatch actions. This leaves the dispatcher in the ModelProxy unused.

case class Actions(dispatcher: Dispatcher) {
  val increase: ReusableFn0[Unit] = ReusableFn0(() => dispatcher(Increase(2)))
  val decrease: ReusableFn0[Unit] = ReusableFn0(() => dispatcher(Decrease(1)))
  val reset: ReusableFn0[Unit] = ReusableFn0(() => dispatcher(Reset))
}
  val actions = Actions(AppCircuit)

  val routerConfig: RouterConfig[Page] = RouterConfigDsl[Page].buildConfig { dsl =>
      ...
      | staticRoute(root, HomePage) ~> renderR { router =>
        AppCircuit.connect { model => model } { proxy =>
          HomeView.Component(HomeView.Props("Home", proxy, actions, router))
        } 
      }
      ...

And finally in the component:

  class Backend(scope: BackendScope[Props, Unit]) {
    def render(props: Props) = {
      <.div(
        <.p("Value = ", <.b(props.proxy.value.counter)),
        ButtonGroup.Component(
          Button(Button.Props(props.actions.increase), "Increase"),
          Button(Button.Props(props.actions.decrease), "Decrease"),
          Button(Button.Props(props.actions.reset), "Reset")))
    }
  }

(In case you are wondering where ReusableFn0, I defined a custom 0-arity ReusableFn. As such type does not exist in scalajs-react, I might be doing something wrong there.)

examples dont build right now

Nice library, looks very promising. I came across a couple of minor issues:

examples/treeview> sbt "~fastOptJS" results in

error sbt.ResolveException: unresolved dependency: me.chrons#diode-core_sjs0.6_2.11;1.0.1-SNAPSHOT: not found

I changed build.sbt to reference "me.chrons" %%% "diode-core" % "1.0.0" instead of "me.chrons" %%% "diode-core" % "1.0.1-SNAPSHOT" and life was ok.

Also, I think http://ochrons.github.io/diode/examples/raf/ is not quite right at the moment... the pulldown is empty so it doesnt seem to do anything

Add debouncing functionality

Quick and dirty rushed implementation. Could be useful for some ideas.

EDIT: Original gist deleted but I dug up an old commit which should work.

import java.util.Date

import diode.ActionResult.NoChange
import diode.data.AsyncAction
import diode.{ActionProcessor, ActionResult, Dispatcher}

class DebounceProcessor[M <: AnyRef] extends ActionProcessor[M] {
  var cleanup = Seq[(Long, AnyRef)]()

  var actionRefLastSent = Map[AnyRef, Long]()
  val delta = 1000

  def clean(now: Long): Unit = {
    cleanup = cleanup.dropWhile { case (itemTime, ref) =>
      actionRefLastSent.get(ref) match {
        case None => {
          // This action has already been cleaned. Drop current item.
          true
        }
        case Some(lastSentTime) => {
          if (itemTime < lastSentTime) {
            // This item has been superseeded by a new action
            true
          }
          else {
            if (lastSentTime + delta <= now) {
              actionRefLastSent -= ref
              true
            }
            else {
              // Item hasn't expired yet
              false
            }
          }
        }
      }
    }
  }


  override def process(dispatch: Dispatcher, action: Any, next: (Any) => ActionResult[M], currentModel: M): ActionResult[M] = {
    val now = new Date().getTime

    clean(now)

    action match {
      case a: AsyncAction[_, _] => {
        if (actionRefLastSent.contains(a)) {
          NoChange
        }
        else {
          actionRefLastSent += a -> now
          cleanup :+= now -> a

          next(action)
        }
      }
      case _ => {
        next(action)
      }
    }
  }
}

React elements not receiving re-render calls on pertinent circuit state changes

I'm having trouble with diode's React interoperability, specifically in changes to a Circuit's state not triggering re-rendering of React elements. However, I fairly new to both Diode and React so the fault may be mine.

I have a widget that holds a list of values:

val DiodeClassificationBox = ReactComponentB[ModelProxy[Pot[PaperInfoShort]]]("ClassBox")
    .render_P { case proxy =>
      <.div(
        TitleBar("Classification"),
        ReadOnlyInputBar(proxy.value.get.mrPrim),
        <.div(
          ^.onMouseUp --> proxy.dispatch(HighlightChange),
          ReadOnlyInputBar((for(s <- proxy.value.get.secondaries) yield s.classCode).mkString(" "))
        )
      )
  }.build
  val ClassificationBox = SelectCircuit.connect(_.paperInfo)(p => DiodeClassificationBox(p))

And another widget that holds a selected value and is only rendered when that selected value matches one of the values in the above list. This selected value is set with the Callback "HighlightChange" above.

val DiodeChangeClassButton = ReactComponentB[ModelProxy[String]]("SaveButton")
    .render_P { case proxy =>
      if(proxy.value != "")
        <.div(
          ^.cursor := "pointer",
          ^.onClick --> proxy.dispatch(ChangeClass(proxy.value)),
          "String is: " + proxy.value
        )
      else
        <.div()
  }.build
  val ChangeClassButton = SelectCircuit.connect(_.selection)(p => DiodeChangeClassButton(p))

Both of these are rendered inside a holder widget.

However, when HighlightChange is fired by making a selection and the selection string is changed in the Circuit state, the ChangeClassButton is not getting a render call (meaning the button does not appear as intended).

Am I incorrect in thinking that all element connected into SelectCircuit's "_.selection" should be getting a rerender call when _.selection changes?

Type safety for connect vs wrap

As a React component that knows whether it needs the ModelProxy to be connected or wrapped, it would be nice if that aspect were type safe. In other words, it would be useful if the component could require a ConnectedModelProxy[A] or WrappedModelProxy[A] instead of ModelProxy[A].

Support for scalajs-react 1.0.0

Right now I'm basically forced to host a verbatim copy of diode-react from a branch in a subdirectory in my project here

Having an official updated artifact (of diode-react) as a dependency would be a lot better.

Subscribe to a zipped model broken ?

Hi guys,

I was wondering if it's an issue or a desired behavior. I'm trying to subscribe to a zipped model but the update is never trigger. I've also try to override the implicits FastEq but nothing can do.

Here is what I've done to create this issue:

 // model
case class Model(name: String, desc: String)
// action
case class ChangeName(name: String) extends Action
case class ChangeDesc(desc: String) extends Action
// circuit
object AppCircuit extends Circuit[Model] {
  def initialModel = Model("", "")

  def actionHandler: AppCircuit.HandlerFunction =
    (model, action) => action match {
      case ChangeName(a) => Some(ModelUpdate(model.copy( name = a )))
      case ChangeDesc(a) => Some(ModelUpdate(model.copy( desc = a )))
      case ChangeBoth(a, b) => Some(ModelUpdate(model.copy(name = a, desc = b)))
    }
}
// test
val nameReader = AppCircuit.zoom(_.name)
val descReader = AppCircuit.zoom(_.desc)
val zipped = nameReader.zip(descReader)

AppCircuit.subscribe(zipped)(a => {
  println(s"Someting has change: ${a.value}") // never called
})
AppCircuit.dispatch(ChangeName("a name"))
AppCircuit.dispatch(ChangeBoth("a name", "a description"))

1.1.6 not published in Maven Repository

Although the README already stated that we should use 1.1.6, it doesn't really work out of the box as 1.1.6 isn't resolved in any repository as of the moment.

Key for iterator on react

If I put Circuit.connect in an iterator I get an error from React of the form

Warning: Each child in an array or iterator should have a unique "key" prop.

This happens even if my wrapped components have a key

I think this is because when doing a connect you endup with multiple instances of a component without a key

I checked the connect code and I think it should contain a call:

.buildU.withKey("mykey").apply()

but I'm not sure what's the best way to pass the key argument. If you have any suggestions I could produce a PR

Publication fails

I tried to publish the latest code but it is failing (at least trying to do a RC) and got a failure
Unfortunately I don't have access to the secrets to change the settings

@ochrons Do you know if something has changed ?

Support action chaining

Given the following code:

Effect.action(Http.call(url).map { result =>
  Action1()
  Action2()
}

It would be useful to be able to chain Action1 and Action2 without having to create an Action3 that combines the two.

Similar to how you have EffectSeq it would be useful to have ActionSeq so we can do:

Effect.action(Http.call(url).map { result =>
  Action1() >> Action2()
}

Effect fails in map

Given the following code:

Effect.action(Action1) >> Effect(longRunningTask.map { result => {
  println("MyAction");
  MyAction(result) 
})

MyAction will not be printed out and MyAction will not be triggered.

Given the following code:

def futureWork: Future[Action] = {
  println("MyAction");
  longRunningTask.map { result => MyAction(result) }
}

Effect.action(Action1) >> Effect(futureWork)

MyAction will be printed out but MyAction will not be triggered.

Scala 3 support

Hello, are there any plans for implementing Scala 3 support?

Request to extract Pot to separate artifact

I'm interested in using Pot within another framework (no offense meant against Diode! I may yet try it). If it were to happen I'm not sure if it would make sense to use Cats's Monad typeclass as a more standardized typeclass (the other framework I'm using, at least, uses Cats).

scalajs-react 1.2 support

I saw that the changes to support scalajs-react 1.2 got merged to master and wondering if it's possible to release a new version with the upgraded dep.

Is the `Traversal` pattern possible with `ModelRW*`?

A toy example. Given the following model structure:

 case class MsgDB(users: Seq[User])
 case class User(id: String, msgs: Seq[String])

and an action:

case class Post(user: String, msg:String)

I'd like to handle a Post action, but only if that user exists. Thinking in a lensy way, I'd like a:

-- | Traverses into a User's message list, if that user exists.
msgsT :: String -> Traversal' MsgDB [String]

is this possible with Diode's Model* pseudo-Lenses?

Ideally I'd have:

val msgsT: ModelRW[MsgDB, Seq[String]] = ...

val postH = new ActionHandler(msgsT) {
  override def handle = {
    case Post(u,m) => updated(value :+ m)  
  }
}

PotAction.handler() type mismatch

The PotAction.handler() function does not work as per the examples - will not compile, Type mismatch.

Taking the example from Scala js App:

case class UpdateMotd(potResult: Pot[String] = Empty) extends PotAction[String, UpdateMotd] {
  override def next(value: Pot[String]) = UpdateMotd(value)
}

class MotdHandler[M](modelRW: ModelRW[M, Pot[String]]) extends ActionHandler(modelRW) {

  override def handle = {
    case action: UpdateMotd =>
      val updateF = action.effect(AjaxClient.welcomeMsg("User X"))(identity _)
      action.handleWith(this, updateF)(PotAction.handler())
  }
}

I see the compiler warning:

Type mismatch, expected: (action, ActionHandler[M, Pot[String]], Effect) => ActionResult[M], actual: (PotAction[Nothing, Nothing], ActionHandler[Nothing, Pot[Nothing]], Effect) => ActionResult[Nothing]

startTime in Pending state is always reset when using PotAction.handler with progress updates

Hi,
when using PotAction.handler(progressInterval) the original startTime is overwritten with every progress update:

case PotPending =>
    if (value.isPending && progressInterval > Duration.Zero)
        updated(value.pending()

the value.pending() goes to

final case class Pending(startTime: Long = Pot.currentTime) {
  override def pending(startTime: Long = Pot.currentTime) = copy(startTime)
}

Which always returns a Pot copy with the current time.

Shouldn't here the startTime be used from the existing instance? I thought to make a PR already but then I've seen that the tests explicitly test for this behaviour.

assert(pend.pending(startTime0).asInstanceOf[PendingBase].startTime == startTime0)
assert(pend.pending().asInstanceOf[PendingBase].startTime > startTime0)

Is my assumption wrong or how can I retrieve the overall duration of the Pending pot.

How to exclude `Pending` pots in PotMap action handler?

Hello,

I'm trying to work around multiple fetching of PotMap data during initial mount/render of React component. The code is very similar to examples here: https://diode.suzaku.io/advanced/PotCollection.html

I've tried to exclude (key, pot) pairs with Pending state from update keys set:

  def handle: PartialFunction[Any, ActionResult[M]] = {
    case update: UpdateUserOrders =>
      val current = value.seq
      val keys = update.keys.filterNot { k =>
        current.exists {
          case (id, pot) if id == k => pot.isPending
          case _ => false
        }
      }

      if (keys.nonEmpty) {
        val updateF = update.effect(AutowireClient[API].getUserOrders(keys).call())(_.mapValues(Ready(_)))
        update.handleWith(this, updateF)(AsyncAction.mapHandler(keys))
      } else noChange
  }

But as soon as I'm adding noChange as empty key set action result, model stops to update at all and stays in Pending state forever.

Could you point to me what I'm doing wrong? Or maybe there is a better way to stop unnecessary updates of common keys?

[Pot] Pending(x).map(...) and .flatMap(...) should return Pending(x) instead of Empty

Example for rationale:

Root model has a: Pot[A], where A is a case class with some property prop: String. We request a to the server, updating the model to a.pending.

We have a React control that has an <.input bound to a.map(_.prop) (resulting in a Pot[String], and we want to disable it (or display an Ajax animation next to it) if it is Pending.

Similarly for flatMap when prop is an Option.

Pot.pending(): argument startTime is ignored in implementations

Hello.

According to Pot.scala, Pot have an abstract method Pot.pending(startTime: Long = ...).
But, in implementations, argument startTime is ignored. For example, Unavailable contains this:

   override def pending(startTime: Long = new Date().getTime) = Pending()

Ready[T] contains similar code:

  override def pending(startTime: Long = new Date().getTime) = PendingStale(x)

Same for Pending, PendingStale and others.

This is designed for some reason or it should be fixed?
Thanks.

Does this have a scaladoc?

If it does, I'm probably not searching hard enough but if not; I can help with creating it.

I find the tutorial quite useful but I think having an API documentation would still work better.

How to reset root model

Dear diode-team,

first of all: This is a really great library. I just have one issue with implementing a model reset functionality. I need a way to reset the ENTIRE root model to the initial state. Sadly, all the zoomTo functions don't allow me to access the root model, because they claim "an illegal field reference". How would I implement an action handler that resets the entire model?

~Karl

Make using actions typesafe

As I tried to move over Diode, one of the errors I faced was passing action type as a parameter instead of an action object. With actions type expected to be of the AnyRef type, there was no help from the compiler. I kept trying to figure out why my handler didn't get invoked.

Perhaps an example (not using Diode, but illustrates the problem, nevertheless) will help (which, btw, happens with Akka and similarly typed systems as well).

def handle: PartialFunction[AnyRef, Unit] = {
  case Foo(i) => println(s"Received Foo with $i")
}

case class Foo(i: Int)

handle(Foo(5))
Received Foo with 5

scala> handle(Foo) // Ouch... no compiler error
scala.MatchError: Foo (of class Foo$)
  at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:253)
  at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:251)
  at $anonfun$handle$1.applyOrElse(<console>:12)
  at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
  ... 33 elided

With Diode, I don't get runtime error either since any unhandled type is eventually silently handled.

The solution is quite simple, I think. Just introduce a marker trait Action and change all AnyRefs expecting an action to Action. Since Diode is very new I don't think that will pose too much burden on people already using it and I doubt that anyone will want to use arbitrary types to signify actions (for example, a String type), so asking to extend Action for all action type seems like a small price to pay for type safety.

On a related note, it will make reading Diode code easier. Currently, there are too many AnyRefs out there and that type doesn't guide in figuring such the code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.