简体   繁体   中英

React Hooks onchange event too slow for scanner input

I'm having an issue with the below pure function capturing scanner input when scanning a barcode. I have a keydown bind occuring in the useeffect as I need to look out for trap codes. When the user types in values it logs correctly, however the scanner seems to be skipping a character or two.

 import React, { useState, useEffect, useRef, useCallback } from 'react'; import * as propTypes from 'prop-types'; import styled from 'styled-components'; const ScannerInputWrapper = styled.input` visibility: hidden; display: none; width: 0%; `; // the current app context would be passed in as a prop to this function function ScannerInputField({ handleScannerInput, isScannerInputDisabled, isDisabled, }) { const [dataEntered, setDataEntered] = useState(''); const [nullarised, setNull] = useState(''); const [scanStarted, setScanStarted] = useState(false); const inputRef = useRef(null); useEffect(() => { document.addEventListener('keydown', handleKeys); return () => { document.removeEventListener('keydown', handleKeys); }; }, [dataEntered]); // focus on the text box at all points. Context awareness will need to come into this. useEffect(() => { inputRef.current.focus(); inputRef.current.select(); }, []); const handleKeys = useCallback(e => { e.preventDefault(); if ((e.shiftKey || e.ctrlKey || e.altKey) && e.key.length > 1) { return; } if (e.key === '<') { setScanStarted(true); return; } if (e.key === '`') { // scan finished, lets send local data to higher function // handleScannerInput(dataEntered); console.log(dataEntered); setScanStarted(false); setDataEntered(''); return; } if (e.key === 'Enter' && !scanStarted && dataEntered !== '') { // scan finished, lets send local data to higher function // handleScannerInput(dataEntered); console.log(dataEntered); setDataEntered(''); return; } if (e.key.length === 1) { const code = e.keyCode ? e.keyCode : e.which; // having to do the below due to running an effect and reading from use state // causes you to read data incorrectly at high velocity const val = dataEntered.concat(String.fromCharCode(code)); setDataEntered(val); } }); return ( <ScannerInputWrapper type="text" onChange={value => setNull(value)} value={dataEntered} disabled={isDisabled || isScannerInputDisabled} ref={inputRef} tabIndex={-1} /> ); } ScannerInputField.propTypes = { handleScannerInput: propTypes.func.isRequired, isScannerInputDisabled: propTypes.bool.isRequired, isDisabled: propTypes.bool.isRequired, }; ScannerInputWrapper.whyDidYouRender = true; export default ScannerInputField; 

I know not everyone will have a scanner, but if anyone can see anything stupid I'm doing I would appreciate the guidance here.

输出测试

在此输入图像描述

Using React v16.8.6

Okay, useState is too async. The solution was to use a reducer instead within a state object on the function. Worked a treat!

https://www.reddit.com/r/reactjs/comments/a3y76f/react_hooks_setstate_gotcha/

Here is the fixed code

 /** * * ScannerInputField * */ import React, { useState, useEffect, useRef, useReducer } from 'react'; import * as propTypes from 'prop-types'; import styled from 'styled-components'; const ScannerInputWrapper = styled.input` visibility: hidden; display: none; width: 0%; `; const initialState = { barcodeInProgress: false, barcodeComplete: false, value: '', }; const keyDownReducer = (state = initialState, e) => { if ((e.shiftKey || e.ctrlKey || e.altKey) && e.key.length > 1) { return state; } if (e.key === '<') { return { ...state, barcodeInProgress: true }; } if (e.key === '`') { return { ...state, barcodeInProgress: false, barcodeComplete: true }; } if (e.key === '_') { return { ...state, barcodeInProgress: false, barcodeComplete: false, value: '', }; } if (e.key.length === 1) { return { ...state, value: state.value + e.key }; } return state; }; // the current app context would be passed in as a prop to this function function ScannerInputField({ handleScannerInput, isScannerInputDisabled, isDisabled, }) { const inputRef = useRef(null); const [state, dispatch] = useReducer(keyDownReducer, initialState); const [nullarised, setNull] = useState(''); useEffect(() => { if (state.barcodeComplete) { handleScannerInput(state.value); // startFromFresh dispatch(new KeyboardEvent('keypress', { key: '_' })); } }, [state.barcodeComplete]); useEffect(() => { document.addEventListener('keydown', handleKeysViaReducer); return () => document.removeEventListener('keydown', handleKeysViaReducer); }, [state]); // focus on the text box at all points. Context awareness will need to come into this. useEffect(() => { inputRef.current.focus(); inputRef.current.select(); }, []); const handleKeysViaReducer = e => { e.preventDefault(); dispatch(e); }; return ( <ScannerInputWrapper type="text" onChange={value => setNull(value)} value={state.value} disabled={isDisabled || isScannerInputDisabled} ref={inputRef} tabIndex={-1} /> ); } ScannerInputField.propTypes = { handleScannerInput: propTypes.func.isRequired, isScannerInputDisabled: propTypes.bool.isRequired, isDisabled: propTypes.bool.isRequired, }; ScannerInputWrapper.whyDidYouRender = false; export default ScannerInputField; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM