I am trying to validate an onchange but can't seem to get it working.
Essentially, I want to test that if the input matches the regex then we present a message under the input.
I am unsure where to put the validator, I wondered if anyone could point me in the right direction
Here is a sandbox.
https://codesandbox.io/s/blazing-hooks-gni5jy?file=/src/components/Dashboard/Dashboard.js
const Dashboard = () => {
const [number, setNumber] = useState(null);
// const [isValid, setIsValid] = useState(false);
// const validator = (value) => {
// if (!value) return false;
// const re = /\b\d{5}\b/g;
// return re.test(value.trim());
// };
const onChangeHandler = (event) => {
const value = event.target.value;
setNumber(value);
};
return (
<div>
<input value={number || ""} onChange={onChangeHandler} />
{/* {isValid ? <p>is valid</p> : null} */}
</div>
);
};
export default Dashboard;
Set the state inside the validator
const [isValid, setIsValid] = useState(true);
const validator = (value) => {
if (!value) return false;
const re = /\b\d{5}\b/g;
const valid = re.test(value);
setIsValid(valid);
};
const onChangeHandler = (event) => {
const value = event.target.value;
setNumber(value);
validator(value);
};
Demo
You will have to call the validator for it to work.
import React, { useState } from "react";
const Dashboard = () => {
const [number, setNumber] = useState(null);
const [isValid, setIsValid] = useState(false);
const validator = (value) => {
if (!value) return false;
const re = /\b\d{5}\b/g;
return re.test(value.trim());
};
const onChangeHandler = (event) => {
const value = event.target.value;
setNumber(value);
setIsValid(validator(value)); // Validating the input
};
return (
<div>
<input value={number || ""} onChange={onChangeHandler} />
{isValid ? <p>is valid</p> : null}
</div>
);
};
export default Dashboard;
Related
In my code, I replace these values
const [items, setItem] = useState<string[]>([]);
const [value, setValue] = useState('')
const [error, setValue]= useState('')
to this
type Props = {
items?: string[],
value?: string,
error?: string
}
and then change the following setItem, setValue, setValue which causes the following error
import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";
import Chip from "#material-ui/core/Chip";
import TextField from "#material-ui/core/TextField";
type Props = {
items?: string[],
value?: string,
error?: string
}
export const TagActions = (props:Props) => {
const { items, value, error } = props;
// const [items, setItem] = useState<string[]>([]);
// const [value, setValue] = useState('')
// const [error, setError]= useState('')
const divRef = useRef<HTMLDivElement>(null)
const handleDelete = (item:any) => {
console.log("handleDelete", item)
const result = items?.filter(i => i !== item)
setItem(result)
};
const handleItemEdit = (item:any) =>{
console.log("handleItemEdit", item)
const result = items?.filter(i => i !== item)
items = result // setItem(result)
value = item // setValue(item)
console.log("value", value)
};
const handleKeyDown = (evt:any) => {
if (["Enter", "Tab", ","].includes(evt.key)) {
evt.preventDefault();
var test = value?.trim();
if (test && isValid(test)) {
items?.push(test)
setValue("")
}
}
};
const isValid = (email:any)=> {
let error = null;
if (isInList(email)) {
error = `${email} has already been added.`;
}
if (!isEmail(email)) {
error = `${email} is not a valid email address.`;
}
if (error) {
setError(error);
return false;
}
return true;
}
const isInList = (email:any)=> {
return items?.includes(email);
}
const isEmail = (email:any)=> {
return /[\w\d\.-]+#[\w\d\.-]+\.[\w\d\.-]+/.test(email);
}
const handleChange = (evt:any) => {
setValue(evt.target.value)
// setError("")
};
const handlePaste = (evt:any) => {
evt.preventDefault();
var paste = evt.clipboardData.getData("text");
var emails = paste.match(/[\w\d\.-]+#[\w\d\.-]+\.[\w\d\.-]+/g);
if (emails) {
var toBeAdded = emails.filter((email:any) => !isInList(email));
setItem(toBeAdded)
}
};
return (
<>
<div>
<TextField id="outlined-basic" variant="outlined"
InputProps={{
startAdornment: items?.map(item => (
<Chip
key={item}
tabIndex={-1}
label={item}
onDelete={() => handleDelete(item)}
onClick={() => handleItemEdit(item)}
/>
)),
}}
ref={divRef}
value={value}
placeholder="Type or paste email addresses and press `Enter`..."
onKeyDown={(e) => handleKeyDown(e)}
onChange={(e) => handleChange(e)}
onPaste={(e) => handlePaste(e)}
/>
</div>
{error && <p className="error">{error}</p>}
</>
);
}
I am a beginner in react typescript, so I don't know how to fix this, Please give me a solution to fix this problem
While changing const to let may fix immediate errors in the console, I doubt this will give you the behaviour that you desire.
The main issue here is that you are mutating the value of props, which in general you should never do. Props are used to pass stateful data down from a parent to a child component. If you wish to update the state of this data from the child component, you should pass an update function using props as well. Below gives an example of what I mean by this by implementing the delete item function (no typescript, but hopefully it gets the idea across):
const ParentComponent = () => {
const [items, setItems] = useState(["item1", "item2", "item3"])
const deleteItem = (itemToDelete) => {
//here we use the functional update form of setState which is good practise when the new state depends on the old state
setItems((items) => items.filter((item) => item!==itemToDelete))
}
return <ChildComponent items={items}, onDeleteItem={deleteItem} />
}
const ChildComponent = ({items, onDeleteItem}) => {
//e.g. to delete item2 call
onDeleteItem("item2")
}
This is one of the more confusing patterns in React, but it is very important to get your head around. Only the component where state is declared should actually be updating that state - as it is the only place where you have access to the setState function.
So I'm not sure why my code isn't taking into subsequent keydowns, it takes the first 'l' then just stops:
I honestly don't know what the issue is. The lorem is a long string that I turn into an array, then if "e.key" === array item at 0, remove that item and update position.
I don't know why this isn't being done for every keydown.
import { useState } from "react"
const Input = ({text}) => {
const [lorem,changeString] = useState(text.split(''))
const [input,changeInput] = useState('')
const [position,changePosition] = useState(0)
const [color,changeColor] = useState(false)
const keyDown = (e) => {
changeInput(e.key)
compareCharacters(input)
}
const compareCharacters = (input) => {
if (input === lorem[position]) {
changeString(lorem.splice(0,1))
changePosition(position+1)
} else {
changeColor(true)
}
}
return (
<input className={`text ${color ? 'new' : ''}`}
type="text"
size="10000"
onKeyDown={keyDown}
>
</input>
)
}
export default Input
import { useState } from 'react'
const Input = ({ text }) => {
const [lorem, changeString] = useState(text.split(''))
const [position, changePosition] = useState(0)
const [color, changeColor] = useState(false)
const keyDown = (e) => {
if (e.key !== lorem[position]) {
changeColor(true)
return
}
changeString(lorem.slice(1))
changePosition(position + 1)
}
return (
<input
className={`text ${color ? 'new' : ''}`}
type="text"
size={10000}
onKeyDown={keyDown}
></input>
)
}
export default Input
I'm trying to display a new, filtered array that hides the rest of the elements and leaves only the ones I type in the search bar. The const newFilter works in the console but doesn't read in the return. I tried placing the const in other places but it's beyond the scope..
import React, { useState } from "react";
function SearchBar({ placeholder }) {
const [filteredData, setFilteredData] = useState([]);
const [wordEntered, setWordEntered] = useState("");
const [pokemonData, setPokemonData] = React.useState({});
React.useEffect(() => {
fetch(
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
)
.then((res) => res.json())
.then((data) => setPokemonData(data.pokemon));
}, []);
const allPokes = pokemonData;
const pokemons = Object.values(allPokes);
const handleFilter = (event) => {
const searchWord = event.target.value;
setWordEntered(searchWord);
const newFilter = pokemons.filter((value) => {
return value.name.toLowerCase().includes(searchWord.toLowerCase());
});
if (searchWord === "") {
setFilteredData([]);
} else {
setFilteredData(newFilter);
}
console.log(newFilter);
};
let checkConsole = () => alert("Check the console :)");
return (
<div className="search-div">
<p className="search-text">Name or Number</p>
<div className="search">
<div className="searchInputs">
<input
type="text"
placeholder={placeholder}
value={wordEntered}
onChange={handleFilter}
/>
</div>
</div>
</div>
);
}
export default SearchBar;
In the given snippet, there is no filtered data displaying logic inside the return
import React, { useState } from "react";
export default function SearchBar({ placeholder }) {
const [filteredData, setFilteredData] = useState([]);
const [wordEntered, setWordEntered] = useState("");
const [pokemonData, setPokemonData] = React.useState({});
React.useEffect(() => {
fetch(
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
)
.then((res) => res.json())
.then((data) => setPokemonData(data.pokemon));
}, []);
React.useEffect(() => {
console.log(filteredData);
});
const allPokes = pokemonData;
const pokemons = Object.values(allPokes);
const handleFilter = (event) => {
const searchWord = event.target.value;
setWordEntered(searchWord);
const newFilter = pokemons.filter((value) => {
return value.name.toLowerCase().includes(searchWord.toLowerCase());
});
if (searchWord === "") {
setFilteredData([]);
} else {
setFilteredData(newFilter);
}
console.log(newFilter);
};
let checkConsole = () => alert("Check the console :)");
return (
<div className="search-div">
<p className="search-text">Name or Number</p>
<div className="search">
<div className="searchInputs">
<input
type="text"
placeholder={placeholder}
value={wordEntered}
onChange={handleFilter}
/>
</div>
/* Add filteredData logic in the return */
{filteredData.map((each) => (
<p>{each.name}</p>
))}
</div>
</div>
);
}
I'm stuck trying to understand why my state won't update until I change the value in the text input twice (calling the handleChange function). What am I doing wrong here?
import React, {useEffect, useState} from "react";
export default function Typeahead(props){
const {list} = props;
const [colorList] = useState(list.map(element => element.toLowerCase()));
const [color,setColor] = useState();
const [showResults, setShowResults]= useState(false);
const [results,setResults]= useState();
let handleChange = (e) =>{
setShowResults(true);
setColor(e.target.value.toLowerCase());
const match = (colorList) =>{
return colorList.startsWith(color,0);
};
const matches = colorList.filter(match);
setResults((matches));
console.log(results);
console.log(showResults);
};
useEffect(() => {
//setResults(list.map(elements => elements.toLowerCase()));
}, [results]);
return(
<div>
<input type= "text" onChange={handleChange}/>
{showResults ?
<div>
{results.map((options) => {
return (
<option key={options} value={options}> {options}</option>
)
})}
</div>
: null }
</div>
);
}
Attaching my code - built using create-react-app.
Working on a small component for searching inputs. Highlight word if match and continue to display other text if not matched.
Current solution displays mark on all text once called.
import React, { useState } from "react";
const app = (props) => {
const [value, setValue] = useState("");
const [searchValue, setSearchValue] = useState("");
const [checked, setChecked] = useState(false);
const [sensitive, setSensitive] = useState("i");
let highlight = null;
const handleChange = (event) => {
setValue(event.target.value);
};
console.log(value);
const handleSearchChange = (event) => {
setSearchValue(event.target.value);
};
console.log(searchValue);
const getHighlightedText = (value, searchValue) => {
let regex = new RegExp(`(${searchValue})`, `g${sensitive}`);
console.log(regex);
const parts = value.split(regex);
//console.log(parts);
highlight = <span> { parts.map((part, i) =>
<span key={i} style={part === searchValue ? { backgroundColor: 'Yellow' } : {} }>
{ part }
</span>)
} </span>;
}
const checkedTest = () => {
if(checked === true) {
setSensitive(" ") // makes it case sensitive
console.log(sensitive);
setChecked(true);
} else {
setSensitive("i")
console.log(sensitive);
setChecked(false);
}
}
return (
<>
<form className="text-search-form">
<textarea className="source-text" value={value} onChange={handleChange}/>
<input className="search-term" value={searchValue} onChange={handleSearchChange} onKeyPress={getHighlightedText(value, searchValue)} />
<label htmlFor="caseSensitive">case sensitive?
<input
type="checkbox"
className="case-sensitive"
name="caseSensitive"
defaultChecked={checked}
onClick={getHighlightedText(value, searchValue)}
onChange={checkedTest}
/>
</label>
</form>
<div className="result">{highlight}</div>
</>
);
};
export default Highlighter;
Adjusted the code to display my proper component name.
This should work as expected
Removed all manual invocation of getHighlightedText and instead moved it to JSX and removed its parameters (May not be a good idea). The reason is that the values getHighlightedText depends on are all in the function state and change of any would trigger a rerender which would automatically call this function.
Secondly, fixed your checkedTest, this is more cleaner.
Thirdly, the comparison you were doing in the iteration of parts wasn't case insensitive, so even if your regex was case insensitive, === of string isn't so, moved it to a seperate function and handled both cases. The insensitive case was handled by converting both part and searchvalue to a common case, in this case, lowercase.
As always, this can be improved so much more, but I think it solves your issues.
Demo:
codesandbox
import React, { useState } from "react";
const App = props => {
const [value, setValue] = useState("");
const [searchValue, setSearchValue] = useState("");
const [checked, setChecked] = useState(false);
const [sensitive, setSensitive] = useState("i");
const handleChange = event => {
setValue(event.target.value);
};
const handleSearchChange = event => {
setSearchValue(event.target.value);
};
const getHighlightColor = part => {
let isEqual = false;
if (checked) {
isEqual = part === searchValue;
} else {
isEqual = part.toLowerCase() === searchValue.toLowerCase();
}
return isEqual ? "Yellow" : "transperant";
};
const getHighlightedText = () => {
let regex = new RegExp(`(${searchValue})`, `g${sensitive}`);
const parts = value.split(regex);
if (searchValue)
return (
<span>
{parts.map((part, i) => (
<span key={i} style={{ backgroundColor: getHighlightColor(part) }}>
{part}
</span>
))}
</span>
);
};
const checkedTest = event => {
setChecked(event.target.checked);
setSensitive(event.target.checked ? "" : "i");
};
return (
<>
<form className="text-search-form">
<textarea
className="source-text"
value={value}
onChange={handleChange}
/>
<input
className="search-term"
value={searchValue}
onChange={handleSearchChange}
/>
<label htmlFor="caseSensitive">
case sensitive?
<input
type="checkbox"
className="case-sensitive"
name="caseSensitive"
defaultChecked={checked}
onChange={checkedTest}
/>
</label>
</form>
<div className="result">{getHighlightedText()}</div>
</>
);
};
export default App;
If you change following component and callback, it should work.
1) onChange callback, get the user selected option (true/false)
2) In the callback, call the search function.
Hope this helps.
<input
type="checkbox"
className="case-sensitive"
name="caseSensitive"
defaultChecked={checked}
onChange={event => checkedTest(event.target.checked)}
/>;
const checkedTest = isChecked => {
setChecked(isChecked);
setSensitive(isChecked ? " " : "i");
// call the getHighlightedText
getHighlightedText(value, searchValue);
};