在扫描条形码时,以下纯功能捕获扫描器输入时出现问题。我有一个keydown绑定发生在useeffect中,因为我需要注意陷阱代码。当用户输入值时,它会正确记录日志,但是扫描仪似乎正在跳过一个或两个字符。
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;
我知道不是每个人都会有扫描仪,但是如果有人看到我正在做的任何愚蠢的事情,请在此提供指导。
使用React v16.8.6
答案 0 :(得分:0)
好的,useState太异步了。解决的办法是在函数的状态对象内改用reducer。工作了请客!
https://www.reddit.com/r/reactjs/comments/a3y76f/react_hooks_setstate_gotcha/
这是固定代码
/**
*
* 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;