React: can't type accents or set the cursor in the middle of a word when using redux or dexie.js
Asked Answered
E

1

0

If the input of a field must be equal to a global state, I usually do something like this (here in dexie.js, but I have similar issues with redux as well):

export const Friend = memo(({id}) => {
    const friend = useLiveQuery(() =>
        db.friends.get(id)
    );
    const deleteFriend = useCallback(() => db.friends.delete(id), [id]);
    const changeName = useCallback((e) => db.friends.update(id, {name: e.target.value}), [id]);
    return <>Name: {friend?.name}, Age: {friend?.age} <input value={friend?.name} onChange={changeName} /> <button onClick={deleteFriend}>Delete</button></>
})

The problem is that with this:

  1. I can't type accents that need two key presses (dead key), like ï
  2. even more annoying, I can't type something in the middle of the word, as the cursor is automatically pushed at the very end:

Here is an example where I moved my cursor to Th*is is a test (* being the position of the cursor) and typed ab ï:

enter image description here

This seems to be related to the fact that react does not handle async updates… but then what would be the recommended way to solve this issue?

MWE

You can find a simple working example here https://github.com/tobiasBora/bug-dexie.

Endanger answered 3/11, 2023 at 16:29 Comment(0)
A
1

Both issues are cause by the same root cause, which is that the cursor will jump to the end on any change.

This is not related to Dexie, nor Redux. You have a controlled input, which means that the value of the input is set from outside the input. It makes sense that the cursor jumps to the end, what if the text changes completely? It wouldn't make sense to keep the same cursor location.

There are several workaround in the answers of React controlled input cursor jumps

Some notes

  1. No need to use async callbacks in useLiveQuery, it's not used in the documentation.
  2. No need to use useCallback if you pass that closure to a host element (, , etc). Host elements won't memoize the callbacks. You are actually slightly hurting the performance by doing so. You can read this for more details
Antoineantoinetta answered 20/12, 2023 at 13:19 Comment(5)
Thanks… unfortunately the answer you linked to only solves the problem of the cursor jumping, but it does not solve the problem of the accents. If I type an accent involving a dead key, it will produce two characters instead of one. I tried a similar solution in svelte, and so far it seems to handle asynchronous entries just fine, so that's really bad react cannot deal with this basic use case.Endanger
Regardless of the accent problem, I think updating the DB on every key stroke could lead to some issues. You could debounce it so that it would only update the DB after there was no keystroke for some time, this way it will likely wait until accent is processed and solve the issue. Input value would be intialized with the DB value, but not kept in sync (apart from the debounced saving)Antoineantoinetta
The problem is that the data may be changed from elsewhere in some cases, for instance if I do an undo operation, so I do need two-way sync.Endanger
You could still subscribe to the DB for changes and override the input value WHEN there is a change. Not sure if there a way to pass an imperatively called callback to Dexie, but worst case, you can still use useLiveQuery and imperatively change the input value when you detect a change in its result (with useEffect)Antoineantoinetta
Yeah, I guess I can make a rather complex component that gets this right if I use debouncing + useEffect indeed, but I need to combine quite a lot of stuff, like the solution you linked above for the position (otherwise the useEffect might still change the position of the cursor) + debounce + the user would need to type the accent before the debouncing occurs… but seems doable, thanks.Endanger

© 2022 - 2024 — McMap. All rights reserved.