Home > other >  How can I track when a user uses an input field to stop the backend from updating an input field whe
How can I track when a user uses an input field to stop the backend from updating an input field whe

Time:09-10

Often our database updates the forms/files/input fields that our users are working on live (e.g. a background job updates the fields with new info).

The form updates right in front of their eyes which they enjoy.

However if they're typing into a specific input field as the update runs, whatever they were typing in that moment gets overwritten, which is annoying.

We would rather whatever the user was writing would take priority and overwrite what the db is pushing upwards into that input field.

A solution I'm hoping for is temporarily storing whatever the user is typing in case an update happens so that we can put that local state back into the input field after they finish typing. This should overwrite whatever the db sent.

How would you do this?

Is it possible to somehow track which input field the user is touching and just protect that specific input field from the db field and not the other fields not currently being modified by the user?

E.g something like this: (I'm mixing in pseudo code here):

<Input
  disabled={!canEdit}
  value={

     if(userIsTypingHere){
      currentLocalInput
     } 
     else{dbData?.docTitle


    }
  placeholder="Title (mandatory)"
  fontSize={'xl'}
  isInvalid={!dbData?.title}
  onChange={(e) =>
    setOurDoc({
      ...dbData,
      docTitle: e.target.value,
    })
  }
/>

<Input
  disabled={!canEdit}
  value={

     if(userIsTypingHere){
      currentLocalInput
     } 
     else{dbData?.docDecription


    }
  placeholder="Title (mandatory)"
  fontSize={'xl'}
  isInvalid={!dbData?.description}
  onChange={(e) =>
    setOurDoc({
      ...dbData,
      docTitle: e.target.value,
    })
  }
/>

The alternative is storing the state from the backend, a temporary holding state and a final state that is presented to the user. But this could get messy quickly and I would rather use the solution above.

Thoughts? ** Extra Info:** We're using: NextJs Chakra UI

Side note: After this is done if the user input overwrites the db update this new input is saved to the db automatically and so will stay persistent.

CodePudding user response:

It seems like 'userIsTypingHere' should act like dirty flag - set it on true when the user starts typing - on change, and back on false when the input is blurred, or maybe after a few seconds, or on save - depending on your business case. You'd probably need a state for your controlled input.

You could update the input in a useEffect if the userIsTypingHere is false.

I see it something like this:

const [userIsTypingHere, setUserIsTypingHere] = useState(false); 
const [currentLocalInput, setCurrentLocalInput] = useState(false);  

useEffect(() => {
  if(!userIsTypingHere) setCurrentLocalInput(dbValue);
}, [dbValue]);

return (
  <Input 
     value={userIsTypingHere ? currentLocalInput : dbValue}
     onChange={(e) => {setUserIsTypingHere(true); setCurrentLocalInput(e.target.value);}}
     onBlur={(e) => {setUserIsTypingHere(false); doStuff(); }} // save to backend or manage conflict
   />

CodePudding user response:

So you want to set value of input to value from DB unless the user is typing? Then we need to define what we mean by user is typing. You can take such thing: After changing input value for first time, if user pauses between typing letters for more than N second, then he/she stopped typing. Then when value comes from DB if user is not typing, only in that case we store it.

// If after changing input value for first time, user doesn't type something longer than this it means they stopped typing
let THRESHOLD = 2000;

export default function App() {
  let [value, setValue] = React.useState('');
  let isTyping = React.useRef();    
  let timeout = React.useRef();

  React.useEffect(() => {
    // Imitate data came from DB. But we store it only if user is not typing now.
    setTimeout(() => !isTyping.current && setValue('DB value'), 5000);       
  }, []);

  return (
    <div>
      <input
        value={value}
        onChange={(e) => {
          if (timeout.current) clearTimeout(timeout.current);

          isTyping.current = true;
          timeout.current = setTimeout(() => {
            isTyping.current = false;
          }, THRESHOLD);

          setValue(e.target.value);
        }}
      />
    </div>
  );
}
  • Related