Right now my search function is I have to click the search icon to make it appear the result so I want to change that to real-time search. When I type the name in input it will auto-starting appear the user card for me
Here is my following code:
const [searchQuery, setSearchQuery] = useState("");
const handleChange = (event) => {
event.preventDefault();
setSearchQuery(event.target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
const res = await axios.get(
`/api/v1/search/users/invite/${searchQuery}/${teamId}`
);
setInvitees(res.data[0]);
setShowInvitees(!showInvitees);
};
useEffect(() => {
if (searchQuery === "") {
setInvitees([]);
}
}, [searchQuery]);
<form onSubmit={handleSubmit}>
<div className="invitees-search">
<Button
className="input invitees--search-icon"
style={{ color: "white", backgroundColor: "#00B790" }}
type="submit"
>
<SearchIcon />
</Button>
<input
className="invitees--search_input"
type="text"
name="name"
onChange={handleChange}
placeholder="Name"
aria-label="Search bar"
pattern="^[a-zA-Z0-9 ]+"
required
/>
</div>
</form>
How can I make it auto-populate when my search query length is >= 2 letters?
You can do so with the help of useEffect
Explanation:
I renamed your handleSubmit function to getInvitees
Calling getInvitees function when searchQuery length is more than or equal to 2
Solution
useEffect(() => {
if(searchQuery === "") {
setInvitees([]);
}
if((searchQuery||'').length >= 2) {
getInvitees();
}
}, [searchQuery]);
const getInvitees = async () => { // renamed handleSubmit function to getInvitees
const res = await axios.get(`/api/v1/search/users/invite/${searchQuery}/${teamId}`);
setInvitees(res.data[0]);
setShowInvitees(!showInvitees);
};
Related
In this mini deposit app I need to validate the input field for 3 different things. With error message that states "Can not be blank", "only numerical values", and "no negative numbers". I have the functionality but tried to implement validation and failed. This is the code without any validation.
function Deposit(){
const ctx = React.useContext(UserContext);
const [totalState, setTotalState] = React.useState(0);
let transactionState = 0; // state of this transaction
let status = `Current Balance $ ${totalState}`;
console.log("Render Account");
const handleChange = event => {
console.log(`handleChange ${event.target.value}`);
transactionState = Number(event.target.value);
};
const handleSubmit = (event) => {
setTotalState(totalState + transactionState);
event.preventDefault();
};
const ATMDeposit = ({ onChange }) => {
return (
<label className="label huge">
Deposit Amount:
<input type="number" onChange={onChange}></input>
<input type="submit" className="btn btn-light" value="Deposit"></input>
</label>
);
};
return (
<Card
bgcolor="primary"
header="Deposit"
body={(
<>
<form onSubmit={handleSubmit}>
<h3>{status}</h3>
<ATMDeposit onChange={handleChange}> Deposit</ATMDeposit>
</form>
</>
)}
/>
)
}
I have implemented the validation code suggested, greatly appreciated, and now validation works! I only need to figure out why the functionality isn't working. The value gets taken from handleChange but it does not add it to setTotalState. I tried adding Number constructor in handleChange like I originally had but it did not work. This is what I have now.
function Deposit(){
const [show, setShow] = React.useState(true);
const [errors, setErrors] = React.useState([]);
const [num, setNum] = React.useState('');
const [totalState, setTotalState] = React.useState(0);
const ctx = React.useContext(UserContext);
let transactionState = 0; // state of this transaction
let status = `Current Balance $ ${totalState}`;
console.log("Render Account");
function validate(value){
const errorsList = [];
if (!value.trim()){
errorsList.push('Error: Cannot be empty');
}
if (/^[-0-9]*$/.test(value) === false){
errorsList.push('Error: only numbers');
}
if (/^[-0-9]*$/.test(value) === true && value < 0) {
errorsList.push('Error: negative values not allowed');
}
if (errorsList.length) {
setErrors(errorsList);
setTimeout(() => setErrors(''),3000)
return false;
}
return true;
}
const handleSubmit = () => {
if (!validate(num)){
setShow(false);
}
if (validate(num)) {
setTotalState(totalState + transactionState);
event.preventDefault;
}
}
const handleChange = event => {
console.log(`handleChange ${event.target.value}`);
transactionState = setNum(event.target.value);
}
return(
<Card
bgcolor="primary"
header= "Make deposit"
status={errors}
body={(
<>
<h3>{status}</h3>
Deposit Amount<br/>
<input type="input" className="form-control" id="num" value={num} onChange={handleChange}></input><br/>
<button type="submit" className="btn btn-light" onClick={handleSubmit}>Deposit</button>
</>
)}
/>
)
}
I would change errors to array
const [errors, setErrors] = React.useState([]);
and here is validation function
function validate(value){
const errorsList = [];
if (!value.trim()){
errorsList.push('Error: Cannot be empty');
}
if (/^[-0-9]*$/.test(value) === false){
errorsList.push('Error: only numbers');
}
if (/^[-0-9]*$/.test(value) === true && value < 0) {
errorsList.push('Error: negative values not allowed');
}
if (errorsList.length) {
setErrors(errorsList);
setTimeout(() => setError(''),3000)
return false;
}
return true;
}
validate("1!") // false
validate("123") // true
Hope this will help
If I leave this as a blank field, this will cause the total to display as isNaN and I don't want the form to be submitted if it is an isNaN. How do I prevent the form from being submitted if the total value shows as isNaN?
export default function BasicTextFields() {
const [fee, setFee] = useState(0);
const amount = parseInt(1000);
const total = Number(amount) + Number(fee);
const handleSubmit = async (e) => {
e.preventDefault();
console.log(" submit");
};
return (
<form onSubmit={{ handleSubmit }}>
<TextField
label="Fee"
type="number"
value={fee}
onChange={(e) => setFee(parseInt(e.target.value))}
InputProps={{
inputProps: {
min: 0
}
}}
/>
<button type="submit">Submit</button>
<br />
Total: {total}
</form>
);
}
codesandbox: https://codesandbox.io/s/basictextfields-material-demo-forked-7sfdph?file=/demo.js:171-809
You can use
onChange={(e) => {
if(e.target.value===""){
setFee(0);
}else{
setFee(parseInt(e.target.value))}}
}
import React, { useState } from "react";
import Box from "#mui/material/Box";
import TextField from "#mui/material/TextField";
export default function BasicTextFields() {
const [fee, setFee] = useState(0);
const amount = parseInt(1000);
const total = Number(amount) + Number(fee);
// async is not required if you are not using await keywork in function.
const handleSubmit = (e) => {
e.preventDefault();
if (isNaN(fee)) {
console.log("its not a number");
} else {
console.log("its a number");
// this submit will refresh page.
// e.target.submit();
// if you do not want page to refresh.
// remeber there is different approch for image data.
var formData = new FormData();
formData.append('fee', fee);
console.log(formData);
// continue with sending data to your backend server
}
};
const changeFee = (e) => {
setFee(e.target.value);
}
return (
// use only one curly braces
<form onSubmit={handleSubmit}>
<TextField
label="Fee"
type="text"
value={fee}
onChange={changeFee}
InputProps={{
inputProps: {
min: 0
}
}}
/>
<button type="submit">Submit</button>
<br />
Total: {total}
</form>
);
}
read the all comments mentioned in above code maybe it helps.
There is a component:
import React, { useEffect, useState } from "react";
import { useParams } from "react-router";
import { NavLink } from "react-router-dom";
const EditIntern = () => {
const { id } = useParams();
const [intern, setIntern] = useState([]);
const [name, inputName] = useState("");
const [email, inputEmail] = useState("");
const [start, inputStart] = useState("");
const [end, inputEnd] = useState("");
const [errorNameEmpty, isErrorNameEmpty] = useState(true);
const [errorEmailEmpty, isErrorEmailEmpty] = useState(true);
const [errorEmailValid, iserrorEmailValid] = useState(false);
const [errorStartEmpty, isErrorStartEmpty] = useState(true);
const [errorEndEmpty, isErrorEndEmpty] = useState(true);
const validEmail = new RegExp(
/(\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)/gm
);
const dateValidate = () => {
if (start.value > end.value) {
console.log("Start > end");
console.log(start.valueAsNumber);
console.log(end.valueAsNumber);
} else {
console.log("Ok");
console.log(start.valueAsNumber);
console.log(end.valueAsNumber);
}
};
useEffect(() => {
const fetchIntern = async () => {
const response = await fetch(`http://localhost:3001/interns/${id}`);
const intern = await response.json();
setIntern(intern);
};
fetchIntern();
console.log(`I want to get intern with id: ${id}!`);
}, [id]);
return (
<div>
<NavLink to="/">Back to list </NavLink>
<form>
<label>Name</label>
<input
type="text"
name="name"
value={name}
onChange={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
} else {
isErrorNameEmpty(false);
}
inputName(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
}
}}
/>
{errorNameEmpty ? <span>Name can't be empty</span> : <></>}
<label>Email</label>
<input
type="text"
name="email"
value={email}
onChange={(e) => {
if (e.target.value === "") {
isErrorEmailEmpty(true);
} else if (!validEmail.test(e.target.value)) {
iserrorEmailValid(true);
isErrorEmailEmpty(false);
} else {
iserrorEmailValid(false);
isErrorEmailEmpty(false);
}
inputEmail(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorEmailEmpty(true);
}
}}
/>
{errorEmailEmpty ? <span>Email can't be empty</span> : <></>}
{errorEmailValid ? <span>Example: email#gmail.com</span> : <></>}
<label>Start date</label>
<input
type="date"
name="email"
value={start}
onChange={(e) => {
if (e.target.value === "") {
isErrorStartEmpty(true);
} else {
isErrorStartEmpty(false);
}
inputStart(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorStartEmpty(true);
}
}}
/>
{errorStartEmpty ? <span>Start date can't be empty</span> : <></>}
<label>End date</label>
<input
type="date"
name="email"
value={end}
onChange={(e) => {
inputEnd(e.target.value);
if (e.target.value === "") {
isErrorEndEmpty(true);
} else {
isErrorEndEmpty(false);
}
dateValidate();
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorEndEmpty(true);
}
}}
/>
{errorEndEmpty ? <span>End date can't be empty</span> : <></>}
<input type="submit" value="Submit" />
</form>
<h2>{intern.name}</h2>
<h2>{intern.email}</h2>
<h2>{intern.internshipStart}</h2>
<h2>{intern.internshipEnd}</h2>
</div>
);
};
export default EditIntern;
It has two inputs of type date. The task is to check that the start date is not greater than the end date.
I created a dateValidate function for this (which doesn't work as it should).
How can you solve this problem? (Perhaps my approach to form validation is generally not correct, I’ll be happy to read about my mistakes)
transform start date and end date to milliseconds new Date(start date).getTime() than compare it (>, <, >=) to the end date also transformed into milliseconds
first select right property
// inputEnd(e.target.value);
if (!isNaN(e.target.valueAsNumber)) inputStart(e.target.valueAsNumber);
// ...
//...
// inputEnd(e.target.value);
if (!isNaN(e.target.valueAsNumber)) inputEnd(e.target.valueAsNumber);
then validate
// ..when user submits start validation
const onFormSubmit = (e) => {
e.preventDefault()
// ...
if (start < end) {
// blah blah....
}
}
or better add min and max value so you dont have to check all
<input type="date" name="start" min="2017-01-01" max="2022-01-01">
also please dont use onChage with onClick event listeners its very bad practice
use either one of them, in this case onChage is pretty good.
seems use are using onClick for validating, use useEffect for validating and keep separate state for errors (object).
and check more options here MDN docs and play around with devtools (right click on element, select use in console option and see what properties it holds)
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 #):
TS Playground link
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
I want to debounce Formik <Field/> but when I type in the field seems debounce does not work. Also I have tried lodash.debounce, throttle-debounce and the same result. How to solve this?
CodeSandbox - https://codesandbox.io/s/priceless-nobel-7p6nt
Snippet:
import ReactDOM from "react-dom";
import { withFormik, Field, Form } from "formik";
const App = ({ setFieldValue }) => {
let timeout;
const [text, setText] = useState("");
const onChange = text => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => setText(text), 750);
};
return (
<Form>
<Field
type="text"
name="textField"
placeholder="Type something..."
onChange={e => {
onChange(e.target.value);
setFieldValue("textField", e.target.value);
}}
style={{ width: "100%" }}
/>
<br />
<br />
<div>output: {text}</div>
</Form>
);
};
const Enhanced = withFormik({
mapPropsToValues: () => ({
textField: ""
}),
handleSubmit: (values, { setSubmitting }) => {
setSubmitting(false);
return false;
}
})(App);
ReactDOM.render(<Enhanced />, document.getElementById("root"));
const [text, setText] = useState("");
const [t, setT] = useState(null);
const onChange = text => {
if (t) clearTimeout(t);
setT(setTimeout(() => setText(text), 750));
};
I would like to suggest to move the call inside of timeout function.
const App = ({ setFieldValue }) => {
let timeout;
const [text, setText] = useState("");
const onChange = text => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
setText(text);
//changing value in container
setFieldValue("textField", text);
}, 750);
};
return (
<Form>
<Field
type="text"
name="textField"
placeholder="Type something..."
onChange={e => {
onChange(e.target.value);
}}
style={{ width: "100%" }}
/>
<br />
<br />
<div>output: {text}</div>
</Form>
);
};
Using Custom Hooks
This is abstracted from the answer provided by #Skyrocker
If you find yourself using this pattern a lot you can abstract it out to a custom hook.
hooks/useDebouncedInput.js
const useDebouncedInput = ({ defaultText = '', debounceTime = 750 }) => {
const [text, setText] = useState(defaultText)
const [t, setT] = useState(null)
const onChange = (text) => {
if (t) clearTimeout(t)
setT(setTimeout(() => setText(text), debounceTime))
}
return [text, onChange]
}
export default useDebouncedInput
components/my-component.js
const MyComponent = () => {
const [text, setTextDebounced] = useDebouncedInput({ debounceTime: 200 })
return (
<Form>
<Field
type="text"
name="textField"
placeholder="Type something..."
onChange={(e) => setTextDebounced(e.target.value)}
/>
<div>output: {text}</div>
</Form>
)
}
An Example Using Redux, Fetching, and Validation
Here's a partial example of using a custom hook for a debounced field validator.
Note: I did notice that Field validation seems to not validate onChange but you can expect it onBlur when you leave the field after your debounced update has executed (I did not try racing it or with a long debounce to see what happens). This is likely a bug that should be opened (I'm in the process of opening a ticket).
hooks/use-debounced-validate-access-code.js
const useDebouncedValidateAccessCode = () => {
const [accessCodeLookUpValidation, setAccessCodeLookUpValidation] = useState()
const [debounceAccessCodeLookup, setDebounceAccessCodeLookup] = useState()
const dispatch = useDispatch()
const debouncedValidateAccessCode = (accessCodeKey, debounceTime = 500) => {
if (debounceAccessCodeLookup) clearTimeout(debounceAccessCodeLookup)
setDebounceAccessCodeLookup(
setTimeout(
() =>
setAccessCodeLookUpValidation(
dispatch(getAccessCode(accessCodeKey)) // fetch
.then(() => undefined) // async validation requires undefined for no errors
.catch(() => 'Invalid Access Code'), // async validation expects a string for an error
),
debounceTime,
),
)
return accessCodeLookUpValidation || Promise.resolve(undefined)
}
return debouncedValidateAccessCode
}
some-component.js
const SomeComponent = () => {
const debouncedValidateAccessCode = useDebouncedValidateAccessCode()
return (
<Field
type="text"
name="accessCode"
validate={debouncedValidateAccessCode}
/>
)
}