developit / linkstate

Bind events to state. Works with Preact and React.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Passing Scalar value throws

gamebox opened this issue · comments

On the Linked State section of the website, you say:

For custom event handlers, passing scalar values to the handler generated by linkState() will simply use the scalar value. Most of the time, this behavior is desirable.

But when I write something like this:

(e) => linkState(this, 'creditCard.cardNumber')(ParseCardNumber(e.target.value))

for an onInput handler, I get the following error:

linkstate.es.js:21 Uncaught TypeError: Cannot read property 'nodeName' of undefined
at eval (linkstate.es.js:21)
at Object.onInput [as input] (home.js:139)
at HTMLInputElement.eventProxy (preact.js:96)

It looks like the offending code is the first line of the returned handler:

var t = e && e.target || this,

This will cause e (the passed in value) to pass on the next value e.target which is undefined as is this, so t will end up being undefined.

Wouldn't the correct way to write this code for the intended behavior be the following?

'var t = (e && e.target) || this,`

This is my first issue on this project, but I am currently writing a POC for my company(a Series A funded startup) and am optimistic that we will be moving our Customer-facing apps to Preact from Angular 1.x over the course of the coming year and would like to contribute back to the framework.

Let me know if my suggestion is on the right track, and I'll dash off a PR.

Looks like you add an idea for a mutation fn to allow this to happen in a cleaner way, but with this post-setState callback, I'm assuming that adding that is a long shot. But the following would be nice:

linkState(this, 'creditCard.cardNumber', null, null, ParseCardNumber)

With the fourth null argument being the already approved callback.

@gamebox My hope was for the mutation function yes. It has the important effect of avoiding a wrapping callback shown in your first example, which is really the main goal of linkstate.

Your use-case is one I've run into many times and actually the reason I've been hesitant to merge #1, which proposes a callback that is invoked after state has been updated and a render triggered. We'll probably need to find a solution that resolves both needs.

I just had an idea: valuePath is already an optional third argument to linkState(), and it currently only supports a string path. What if we extended it so you could pass a function, and it would call that function with the determined value and event itself, using the resulting return value as the new state value?

function parseCardNumber(value, event) {
  value = Math.round(value);  // some parsing logic
  return value;
}

<input onChange={
  linkState(this, 'foo', parseCardNumber)
} />

Because the first argument is the value, it basically would act like a "transform" to apply to values before setting them in state.

Then, because the second argument is the event object itself, you could have a transform that instead does something interesting with the event:

function getHeight(value, event) {
  return event.target.scrollHeight;
}

class Demo extends Component {
  render(props, { height }) {
    return (
      <textarea
        style={{ height }}
        onInput={ linkState(this, 'height', getHeight) }
      />
    );
  }
}

Hi, @developit
By any chance is this feature likely to be released/merged through #16?
I think your suggestion of passing a function as the value path is excellent.

At the moment, I don't think there's a way of passing scalar values to linkstate, which is problematic for a scenario using custom components, like WP Gutenberg blocks, as it passes the value directly rather than the event itself.

Thanks