by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jan 31 2019 10:57
  • Jan 30 2019 22:22
    bitpirate starred japgolly/scalajs-react
  • Jan 30 2019 08:11

    japgolly on master

    Update docs for changes in 1.4.0 (compare)

  • Jan 29 2019 13:20
  • Jan 28 2019 08:08
    booleguo starred japgolly/scalajs-react
  • Jan 28 2019 04:09

    japgolly on v1.4.0

    (compare)

  • Jan 28 2019 04:05

    japgolly on master

    Towards v1.4.1 (compare)

  • Jan 28 2019 04:04

    japgolly on master

    Prepare for v1.4.0 Upgrade React to 16.6.3 React.lazy and 85 more (compare)

  • Jan 28 2019 04:04
    japgolly closed #521
  • Jan 28 2019 04:04
    japgolly opened #521
  • Jan 28 2019 04:03

    japgolly on v1.4.x

    Add note about Reusability/Reus… doc formatting Prepare to release v1.4.0 (compare)

  • Jan 28 2019 04:02
    wiitht starred japgolly/scalajs-react
  • Jan 28 2019 03:18

    japgolly on v1.4.x

    Test AsyncCallback.init Update Travis CI caching (compare)

  • Jan 28 2019 03:02

    japgolly on v1.4.x

    Update Travis CI cache dirs Revise changelog (compare)

  • Jan 28 2019 03:00
    japgolly labeled #520
  • Jan 28 2019 03:00
    japgolly labeled #519
  • Jan 27 2019 17:36
  • Jan 26 2019 06:49
    japgolly commented #520
  • Jan 26 2019 05:55
    japgolly commented #519
  • Jan 26 2019 02:35
    cquiroz opened #520
Roberto Leibman
@rleibman
Still haven't figured out my issue, I'm clearly doing something stupid or wrong or both. I have an AppRouter (which uses Router) the menu should change depending on what page you're on, and I want to allow my pages to add/remove their own menu items, I've been trying to do this which Context, but for some reason it didn't work (see my earlier comment from June 23) and it's way too easy when doing this for me to make a change in a downstream component that changes the top component and ends up in an infinite loop... any ideas on how to approach this issue?
Yilin Wei
@yilinwei
@rleibman , [this[(https://github.com/rleibman/chuti/blob/9c4504da0b33bfad5a9a6bf4096cad2a7ccac72b/web/src/main/scala/pages/GamePage.scala#L175) looks odd to me? What's the function application of the Callback doing?
Roberto Leibman
@rleibman
Making a mod to the context such that it will now call the lower page for menu items instead of whatever it was calling before.
Basically, when I a game gets loaded successfully, I change the function that the router will call to pick up menu items.
It's the router's responsibility to render the menu, but the page's responsibility to decide what goes in that (sub) menu.
Øyvind Raddum Berg
@oyvindberg
I had a quick look last time you posted @rleibman , and on a superficial level I thought it should work. The key to working out things like this is minimization. Try to get it down to a small number of lines, and normally it'll become more obvious
Roberto Leibman
@rleibman
I thought of that... these environments don't often lend themselves to smaller examples :) I'll probably do that though.
Roberto Leibman
@rleibman
@oyvindberg so, what I think is going on is that I'm setting up the callbacks, but setting them up causes a re-render, so now the method in the context state is referring to the old component I prevent a rendering infinite loop by checking to see if the menus are already in the state I would want, but it's pointless, because the methods are still referring to a no longer existing component.
I've tried a bunch of things but still end up in either an infinite loop, or pointing to the old component.
Immutability is a harsh mistress.
Øyvind Raddum Berg
@oyvindberg
Thread that change up to a component above which can handle it properly through a callback
Roberto Leibman
@rleibman
I'm not exactly sure what you mean. Here's a reduced start:
object ParentComponent {
  case class ChildProps ()

  private val childComponent = ScalaComponent
    .builder[ChildProps]("child")
    .renderStatic {
      <.div("Hello World")
    }
    .componentDidMount($ => Callback.empty) //TODO need to update parent menu items here, *once*, but without causing a re-render loop
    .build

  def ChildComponent(props: ChildProps): Unmounted[ChildProps, Unit, Unit] = childComponent(props)

  case class ParentState(menuItems: Seq[(String, Callback)] = Seq.empty)

  private val parentComponent = ScalaComponent
    .builder[Unit]("parent")
    .initialState(ParentState())
    .render_S { state =>
      <.div(state.menuItems.toVdomArray(_._1), ChildComponent(ChildProps()))
    }
    .build
}
Roberto Leibman
@rleibman
I think I figured it out, this works, but I will not if it works until I weave it back into my actual application:
https://gist.github.com/rleibman/b3444d3fd7f17fa8763adf1204a9fbfa
Roberto Leibman
@rleibman
:( Though the above example works, once I tried weaving it into my app I get the infinite render loop :( Why would my gist work, considering that addMenuProvider modifies the context state which should make it re-render? I ask because for whatever reason this works, and my actual one doesn't.
Øyvind Raddum Berg
@oyvindberg
You should restructure this code @rleibman , running that callback immediately when the child mounts seems like a recipe for, well, what you're seeing. I would pull things up and have a less powerful child component. You want to initiate that data fetch after an earlier click by the user I imagine
Roberto Leibman
@rleibman
Yeah, I was thinking of pulling the Game all the way to the top component and have it decide on the menu, the only reason I hadn't done that is because I still wanted lower components to be able to add items into the menu. <sigh> that's going to be some work.
Thanks a lot for looking at it, by the way, I really appreciate it.
Roberto Leibman
@rleibman
OK @oyvindberg , got it all working! It's not perfect, as I still haven't figured out if I wanted my child component to change the parent component's menu how I would do that, but by pushing the state all the way to the top it works. Thanks again!
Roberto Leibman
@rleibman

I have this render:

    def render(s: State) =
      VdomArray(
        <.button(^.onClick ==> { _ => playSound(Some("/sounds/campanita.mp3")) }
        })("Sound!"),
        ReactSound(
          playStatus = if (s.soundUrl.nonEmpty && !s.soundMuted) s.soundPlayStatus else STOPPED,
          url = s.soundUrl.getOrElse("")
        )(),
        ChutiState.ctx.provide(s.chutiState) {
          <.div(
            AppRouter.router()
          )
        }
      )

playSound makes a modState to change the url of the sound being played.
For some reason, a component, deep in the AppRouter.router, which has no connection at all to the sound is being re-rendered (it's componentDidMount method is being called) which makes an extra and completely unecessary ajax call to the server.
Why is my component being re-rendered, and how can I stop it?

Am I complicating things too much to play a sound?
Roberto Leibman
@rleibman

...heck yes, this worked:

    def playSound(soundUrlOpt: Option[String]): Callback = {
      soundUrlOpt.fold(Callback.empty) { url =>
        Callback {
          val sound = new Audio(src = url)
          sound.play()
        }
      }
    }

I mean, it still doesn't answer my question of why things are re-rendering, so please still answer, but I've got sounds

Dennis
@dennis4b_twitter
@rleibman componentDidMount should not be called for a re-render but only for the initial mount. Try to debug first why this is happening. (what else gets re-rendered, are props/state different, is the router switching pages forth and back, etc)
Roberto Leibman
@rleibman
In my case:
case class State(something: String, chutiState: ChutiState)
something changes on the click, but not chutiState
Dennis
@dennis4b_twitter
yes (which would cause a re-render on the AppRouter I think, if you're not using Reusable) but somewhere downstream in the router a component is dismounted and re-mounted, which should not happen just because of a re-render (but I don't know what the router does internally)
Roberto Leibman
@rleibman
Nothing magic, just like the examples.... the component that is re-rendering is inside a page, which is rendered by resolution.render()
Roberto Leibman
@rleibman
I think I've been doing this stupidly the whole time and I only now realize it because it's affecting the performance seriously.... if my single page app has one big state, which I pass down either directly or through context.... and everything depends on pieces of that state, everything seems to re-render (i.e. componentDidMount gets called) every time modState gets called on the upper level component, even though only a piece of the whole state really changed.
Carlos Quiroz
@cquiroz
Have you looked at Reusability?
Roberto Leibman
@rleibman
Mhh... I briefly saw it, and it does seem like it would be useful, but I ignored it :) I'll definitely take a look at it now! I'm also aggregating a bunch of distinct $.modState calls into many fewer by using for comprehensions around my asynccallbacks instead.
Roberto Leibman
@rleibman
Is there any way from $.modState(s=>s) >> $.modState(s=>s) to NOT cause two re-renders?
Carlos Quiroz
@cquiroz
I bet you can combine both state updates into a single function
modState(a andThen b)
Roberto Leibman
@rleibman

It's not that simple... I've got two different functions:

def init(): Callback = $.modState(s=>s)
def refresh(): Callback = $.modState(s=>s)

And in onComponentUpdate I call init() >> refresh()

So I know that each of them does a modState, and if I combine them into a single function, they do what I expect, but then I can't reuse the refresh portion.
Roberto Leibman
@rleibman
I'm really confused by the whole lifecycle!!!
I have a websocket... So, I'm supposed to initialize my component through ajax on componentDidMount, I start the socket there, and I clean up the socket in componentWillUnmount. But both of these happen regardless of whether the component was (shouldComponentUpdate, calculated with Reusability)
Roberto Leibman
@rleibman
And to complicate matters... the AppRouter doesn't seem to work like other components, it doesn't respond to Context changes :(
Øyvind Raddum Berg
@oyvindberg
@rleibman re: state you can make those to functions return CallbackTo[State] and "commit" the new state outside, that way the caller is in control
and it still sounds like a parent of your websocket thing is rerendering and causing it to be initialized again
Roberto Leibman
@rleibman
I agree on that, which is why I was pushing the websocket thing all the way to the top, to bypass tat.
So I pushed it to my AppRouter, but the appRouter doesn't seem to respond correctly to state changes.
Øyvind Raddum Berg
@oyvindberg
so the websocket component is rerendered on route changes?
Roberto Leibman
@rleibman
Let me rephrase my statement, I think it is being redrawn, now properly and and ComponentDidMount is being called at the appropriate time... only once, but... The websocket component (a chat) needs to be rerendered when you enter different channels (the old socket is closed, the new one is subscribed to)
Before, I had different chatcomponents in different pages, I moved it to the top, and now it only gets initialized once, which is not enough, before it was being initialized on every refresh, which was way too many.
So I guess my question is. The channelid gets passed in the props, on which lifecycle method should I connect (and disconnect) from the websocket?
Øyvind Raddum Berg
@oyvindberg
componentDidUpdate?
had you tried that?
Roberto Leibman
@rleibman
I was thinking of that... but I think I just figured it out!
Simply change the key!
  def apply(
    user:             User,
    channel:          ChannelId,
  ): Unmounted[Props, State, Backend] = component.withKey(s"chatChannel${channel.value}")(Props(user, channel))
Øyvind Raddum Berg
@oyvindberg
yeah that works