I need to get each user's keystroke when he pressed a certain key("#") and stop getting his keystroke when he pressed other key(space(" ")). For example: a user enters the text "I wanna go to #shop", I need to save his input and the tag inside it. How can I do it? I wrote some code to do it but I don't know how to make it completely
onKeyDown = (e) => {
let value = e.target.value, tags = [], currentTag = "";
if (e.key == "Enter") {
this.setState((state) => {
const item = this.createNote(value, tags);
return { notes: [...state.notes, item] };
});
}
if (e.key == "#") {}
};
You can make use of regex /#[^\s]+/g
Live Demo
export default function App() {
const [value, setValue] = useState("");
const [tags, setTags] = useState([]);
function onInputChange(e) {
const value = e.target.value;
setValue(value);
const tags = value.match(/#[^\s]+/g) ?? [];
setTags(tags);
}
return (
<>
<input type="text" name="" value={value} onChange={onInputChange} />
<ul>
{tags.map((tag) => {
return <li key={tag}> {tag} </li>;
})}
</ul>
</>
);
}
EDITED: You can make use of useMemo hook as
Thanks to 3limin4t0r
Live Demo
export default function App() {
const [value, setValue] = useState("");
const tags = useMemo(() => value.match(/#\S+/g) || [], [value]);
function onInputChange(e) {
const value = e.target.value;
setValue(value);
}
return (
<>
<input type="text" name="" value={value} onChange={onInputChange} />
<ul>
{tags.map((tag) => {
return <li key={tag}> {tag} </li>;
})}
</ul>
</>
);
}
Instead of parsing individual key values, you can use a function like this to parse your input field on changes and return an array of hashtags (without the leading #):
function parseTags (input: string): string[] {
return (input.match(/(?:^#|[\s]#)[^\s]+/gu) ?? []).map(s => s.trim().slice(1));
}
Here's a working example in a functional component which incorporates the function:
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.16.4/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel" data-type="module" data-presets="react">
const {useState} = React;
function parseTags (input) {
return (input.match(/(?:^#|[\s]#)[^\s]+/gu) ?? []).map(s => s.trim().slice(1));
}
function Example () {
const [value, setValue] = useState('');
const [tags, setTags] = useState([]);
const handleChange = (ev) => {
const {value} = ev.target;
setValue(value);
setTags(parseTags(value));
};
return (
<div>
<input
type="text"
onChange={handleChange}
placeholder="Type here"
value={value}
/>
<div>Parsed tags:</div>
<ol>
{tags.map((str, index) => <li key={`${index}.${str}`}>{str}</li>)}
</ol>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
</script>
Something like this should work for you; You can adapt if you don't have access to hooks:
const RecorderInput = ({ onChange }) => {
const [isRecording, setIsRecording] = useState(false);
const toggleRecording = (e) => {
const character = String.fromCharCode(e.charCode);
if (character === '#') {
setIsRecording(true);
}
if (character === ' ') {
setIsRecording(false);
}
}
const handleChange = (e) => {
if (isRecording) onChange(e);
toggleRecording(e);
}
<input type="text" onChange={handleChange} />
}
As other suggested your onChange can also use regex groups to capture hashes as the user types. Thinking about this now, it would probably be a lot cleaner to do it this way but regex is well documented so I won't go through the hassle