yysun / apprun

AppRun is a JavaScript library for developing high-performance and reliable web applications using the elm inspired architecture, events and components.
https://apprun.js.org
MIT License
1.18k stars 57 forks source link

Two-way bound textbox loses focus on input #112

Closed Geordi7 closed 2 years ago

Geordi7 commented 3 years ago

This snippet (tested in playground) shows the issue:

class C extends Component {
    state = {a: {b: {c: ''}}}
    view = s => <div>
        Hello {s.a.b.c}<br />
        <input type='text' value={s.a.b.c}
          $oninput={(s,e) => (s.a.b.c=e.target.value, s)}
          />
      </div>
}

new C().start(document.body)

When the textbox is EMPTY and you type one character, it immediately loses focus.

Geordi7 commented 3 years ago

After further testing it seems the problem is not with the input itself but with the text node before it.

if you replace Hello {s.a.b.c}<br /> with {'Hello ' + s.a.b.c}<br /> it works correctly.

This is probably still a bug.

chancyk commented 3 years ago

@Geordi7 It seems the first approach causes the entire parent div to refresh, which wipes out the input element, killing the focus. Wrapping it in a span so it has its own node also seems to resolve the issue.

const focus = e => e.focus();
class C extends Component {
    state = {a: {b: {c: ''}}}
    view = s => <div>
        <span>Hello {s.a.b.c}</span>
        <input type='text' value={s.a.b.c}
          $oninput={(s,e) => (s.a.b.c=e.target.value, s)}
          />
      </div>
}

new C().start(document.body)

Another solution might be to use a ref so it's refocused when the input element is refreshed:

const focus = e => e.focus();
class C extends Component {
    state = {a: {b: {c: ''}}}
    view = s => <div>
        Hello {s.a.b.c}<br/>
        <input ref={focus} type='text' value={s.a.b.c}
          $oninput={(s,e) => (s.a.b.c=e.target.value, s)}
          />
      </div>
}

new C().start(document.body)

There's an example called Ref - Examples that uses the ref approach for contenteditable also, which I've noticed can be quirky in frameworks also for similar reasons.

chancyk commented 3 years ago

I ran into another good usage for ref. MaterializeCSS relies on input elements having a specific type set, but when type="text" will sometimes get stripped from the element because it's the default, causing the label animations not to work. I was able to use a ref to specifically reset the type attribute which fixed the issue.