Skip to content

e.target null in React 0.14.7 #255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ngbinh opened this issue Feb 4, 2016 · 18 comments
Closed

e.target null in React 0.14.7 #255

ngbinh opened this issue Feb 4, 2016 · 18 comments

Comments

@ngbinh
Copy link

ngbinh commented Feb 4, 2016

https://github.com/facebook/react/releases/tag/v0.14.7

seems to break react.js 0.10.4

    val onEmailChange: ReactEventI => Callback = { e =>
      scope.modState(_.copy(email = e.target.value))
    }
        ^.onChange    ==> backend.onEmailChange,

is broken because somehow e.target is null. Downgrade to react.js 0.14.6 seems to fix it.

@japgolly
Copy link
Owner

japgolly commented Feb 4, 2016

There was this thing React implemented a while back (0.13?) where events would have their properties wiped at the end of the callback unless you call .persist on them. If you were storing that callback and then having another thread (well "timeout") call it, then it would normal behaviour. But in your case you have onChange directly calling .modState so it should work.

This commit here looks related:
facebook/react@3285d83#diff-4bac2e331b5408363d4b836243e4a101

It's weird though because I'm using 0.14.7 in my work project and I haven't had this problem. Are you sure there's nothing else changed?

@ngbinh
Copy link
Author

ngbinh commented Feb 4, 2016

in term of react related, nothing changed! All I did to see the problem was upgrading to react.js to 0.14.7

@japgolly
Copy link
Owner

japgolly commented Feb 4, 2016

That's weird. Ok, need to figure out if problem with React.JS or scalajs-react. If problem with JS, you can confirm quick using a fiddle https://jsfiddle.net/reactjs/69z2wepo/ and if it's a bug, raise on facebook/react directly. If JS is ok then I suggest write a Scala.JS unit test to confirm and demonstrate.

@japgolly japgolly changed the title react.js 0.14.7 e.target null in React 0.14.7 Feb 4, 2016
@japgolly
Copy link
Owner

japgolly commented Feb 4, 2016

Oops that fiddle is 0.14.6. Oh well, just update the JS links to 0.14.7.

@ngbinh
Copy link
Author

ngbinh commented Feb 4, 2016

ok, will look into it. May take some time as I am on vacation.

@japgolly
Copy link
Owner

japgolly commented Feb 5, 2016

No worries. I use 0.14.7 too so I'll keep an eye out.

@ochrons
Copy link

ochrons commented Feb 13, 2016

After I updated SPA tutorial to use 0.14.7, I'm also seeing this same issue in TODO.scala

    def updateDescription(e: ReactEventI) =
      // update TodoItem content
      t.modState(s => s.copy(item = s.item.copy(content = e.target.value)))
.
.
          <.input.text(bss.formControl, ^.id := "description", ^.value := s.item.content,
            ^.placeholder := "write description", ^.onChange ==> updateDescription)),

e.target is null

Going back to 0.14.6 fixes the problem.

@japgolly
Copy link
Owner

Probably a React bug. We aren't doing anything special with events or modState.

@ochrons
Copy link

ochrons commented Feb 14, 2016

Seems like sort of expected behaviour in React (http://ludovf.net/reactbook/blog/reactjs-null-event-target.html). For some reason wasn't that visible before 0.14.7

@ochrons
Copy link

ochrons commented Feb 14, 2016

Modifying the code like this fixes the issue in 0.14.7, too

    def updateDescription(e: ReactEventI) = {
      val text = e.target.value
      // update TodoItem content
      t.modState(s => s.copy(item = s.item.copy(content = text)))
    }

@ngbinh
Copy link
Author

ngbinh commented Feb 15, 2016

hmm, that's strange. If it works, the previous version should work too.

@nafg
Copy link

nafg commented Feb 24, 2016

Maybe they changed how aggressively / eagerly they recycle?

@japgolly
Copy link
Owner

Seems pretty deliberate: facebook/react#5840

This sucks because it's another one of those cases where you need to read the fine print of the docs to avoid a runtime error. Unfortunately, there's nothing I can do to prevent this at compile-time. We'll have to just either live with it or petition upstream to do something about it.

@nafg
Copy link

nafg commented Feb 25, 2016

What about Callback.event helper that returns an event => Callback and
takes an event' => Callback where event' is a case class type in Scala
that's immediately populated from the raw JavaScript object? (Or that
simply calls .persist first, named accordingly.)

On Thu, Feb 25, 2016, 3:29 AM David Barri [email protected] wrote:

Closed #255 #255.


Reply to this email directly or view it on GitHub
#255 (comment).

@japgolly
Copy link
Owner

React actively discourages using .persist so I wouldn't want to build a helper on it. Instead how about tacking a method onto events that allows them to immediately extract the portion they need like

def extract[A, B](f: this.type => A)(g: A => B): B =
  g compose f

which when used looks like:

    def updateDescription(e: ReactEventI) =
      e.extract(_.target.value)(text =>
        t.modState(s => s.copy(item = s.item.copy(content = text)))

It's just a bit of sugar to avoid writing:

    def updateDescription(e: ReactEventI) = {
      val text = e.target.value
      t.modState(s => s.copy(item = s.item.copy(content = text)))
    }

I'm not sure it's worth it.

@nafg
Copy link

nafg commented Feb 26, 2016

Yeah, I'd rather it would just automatically lift it to a Scala case class
that corresponds to the whole event. So it does save boilerplate, but even
more importantly, it helps not expose you to that sort of bug.

On Thu, Feb 25, 2016, 7:40 PM David Barri [email protected] wrote:

React actively discourages using .persist so I wouldn't want to build a
helper on it. Instead how about tacking a method onto events that allows
them to immediately extract the portion they need like

def extract[A, B](f: this.type => A)(g: A => B): B =
g compose f

which when used looks like:

def updateDescription(e: ReactEventI) =
  e.extract(_.target.value)(text =>
    t.modState(s => s.copy(item = s.item.copy(content = text)))

It's just a bit of sugar to avoid writing:

def updateDescription(e: ReactEventI) = {
  val text = e.target.value
  t.modState(s => s.copy(item = s.item.copy(content = text)))
}

I'm not sure it's worth it.


Reply to this email directly or view it on GitHub
#255 (comment)
.

@nafg
Copy link

nafg commented Feb 26, 2016

Also, ==> could be changed to not take a raw ReactEventI etc. but some new
EventCallback type (with deprecated backward compatibility) so users don't
have to do anything extra to be shielded from null.
(Obviously the philosophy about null is different in JavaScript land than
in Scala land.)
In a sense it's very similar to why scala-js-react wraps state in a
Callback: it's a value that's dynamically scoped, and in Scala we prefer
safety.

On Thu, Feb 25, 2016, 10:18 PM Naftoli Gugenheim [email protected]
wrote:

Yeah, I'd rather it would just automatically lift it to a Scala case class
that corresponds to the whole event. So it does save boilerplate, but even
more importantly, it helps not expose you to that sort of bug.

On Thu, Feb 25, 2016, 7:40 PM David Barri [email protected]
wrote:

React actively discourages using .persist so I wouldn't want to build a
helper on it. Instead how about tacking a method onto events that allows
them to immediately extract the portion they need like

def extract[A, B](f: this.type => A)(g: A => B): B =
g compose f

which when used looks like:

def updateDescription(e: ReactEventI) =
  e.extract(_.target.value)(text =>
    t.modState(s => s.copy(item = s.item.copy(content = text)))

It's just a bit of sugar to avoid writing:

def updateDescription(e: ReactEventI) = {
  val text = e.target.value
  t.modState(s => s.copy(item = s.item.copy(content = text)))
}

I'm not sure it's worth it.


Reply to this email directly or view it on GitHub
#255 (comment)
.

@japgolly
Copy link
Owner

The problem is that events are mutable and recycled by React, presumably because it's important for performance. Making a wrapper case class or changing ==> would only solve it if we took an immutable copy of the event which would just add back (and more) the performance impact that motivated FB to make this change in the first place. I'm not keen on imposing a performance degradation on downstream projects. I think calling persist / extracting values yourself is the least worst of the options.

gshakhn added a commit to gshakhn/private-wiki that referenced this issue Apr 15, 2016
* Update react to 15.0.1.
* Use e.extract to get the target of an event to get around japgolly/scalajs-react#255.
  Tests didn't catch this bug. :(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants