How to write an onChange event through hooks? - reactjs

I would like to register the event onChange through Hooks
const { i18n } = useTranslation();
const [locale, setLocale] = useState('en')
const handleChange = (lang) => {
setLocale(lang)
i18n.changeLanguage(lang);
}
tried using the select but ended up choosing Switch
``return (
<div className={styles.wrapper}>
<div className={styles.switchBorder}>
<Switch className={styles.switch} checkedChildren="EN" unCheckedChildren="RU"
checked={locale} onChange={(e) => handleChange(e.target.checked)}
/>
</div>
</div>
)``

const handleChange = ({currentTarget}) => {
const {checked: lang} = currentTarget;
setLocale(lang)
i18n.changeLanguage(lang);}
onChange={handleChange}

const handleChange = () => {
if (locale === 'en') {
setLocale('ru');
i18n.changeLanguage( 'ru');
} else {
setLocale('en');
i18n.changeLanguage( 'en');
}
};

Related

Typescript: How to update the state

In my project, I have an email field to implement using the chip component. But I am facing a problem here, first time, when I paste multiple email values it gets inserted into the field, but second time when I copy some other values and paste them into the field, it replaces the previous values.
In first time:
Secnod time when I paste "abc4#abc.com" :
previous values replace with the current value.
import Chip from "#material-ui/core/Chip";
import TextField from "#material-ui/core/TextField";
import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
export const TagActions = () => {
const [items, setItem] = useState<string[]>([]);
const [value, setValue] = useState("");
const [error, setError] = useState("");
const divRef = useRef<HTMLDivElement>(null);
const [flag, setFlag] = useState(false);
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);
setItem(result);
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)) {
setFlag(true);
// 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");
console.log("pppp", paste);
var emails = paste.match(/[\w\d\.-]+#[\w\d\.-]+\.[\w\d\.-]+/g);
if (emails) {
console.log("inside 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
className={!isEmail(item) ? "chip-tag-error" : "chip-tag"}
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. Please give me a solution to solve this situation.
Append to the list instead of overwriting it like
setItem(i => [...i, ...toBeAdded]);

search in react js : i want to serch my name and get both name and id

i am trying to do a search in react js and its working the only problem is i am not able to get one more value in to my serch result (id)
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
//const [suggestions, setSuggestions] = useState([]);
const suggestions = [];
showSerch.forEach(function (data) {
var data = data.firstname; /////// i pass the name from here i also want to pass id it will look something like this var data = data.id
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter((suggestion) =>
suggestion.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
function finddoctor(e) {}
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<li
style={styles.listyle}
key={index}
className={classname}
onClick={finddoctor(index)}
>
{suggestion} {id } // i want the id passed here
</li>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("my-url")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon style={{ marginRight: "-23px" }} icon={faSearch} />
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
// onClick={() => onSelectSuggestion()}
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
);
};
i am trying to get name and id based on searching my with name but i am able to pass only the name throgh filter
the code works and i am able to get all names based on the search but i also want the id there
I think you can do this:
Use the entire object in the suggestion, like this:
showSerch.forEach(function (data) {
suggestions.push(data);
});
Note: I believe this forEach could be unnecessary.
In the filter method you can compare the value with name or id, like this:
const filteredSuggestions = suggestions.filter((suggestion) => suggestion.name.toString().toLowerCase().includes(value.toLowerCase()) || suggestion.id.toString().toLowerCase().includes(value.toLowerCase()));
In the li tag:
{suggestion.name} {suggestion.id} // i want the id passed here
I'm assuming the suggestion have an id attribute, if not, use the correct one.

React UseState is called twice inside eventListener

I am starting to learn react in my free time. When starting Hooks,the setState method is called twice.
The live code is at
https://codesandbox.io/s/elastic-saha-mdwwc?file=/src/App.js
The Add button works fine.But when I press enter, the setState function is called twice. Initial thought was that the content re rendered.But I have the useEffect dependency as an empty array. So no re-rendering is done and I am not sure how to debug this. Any help is appreciated :)
const App = () => {
const [text, setText] = useState('');
const [list, setList] = useState([]);
const addToList = () => {
if (text !== '') {
setList([...list, text]);
setText('');
}
}
const deleteItem = index => {
const deletedList = list.filter((_, i) => i !== index);
setList(deletedList)
}
useEffect(() => {
console.log("Reloaded");
const listener = e => {
if (e.code === 'Enter') {
console.log("event triggered")
setText(text => {
if (text !== '') {
console.log("updating")
setList(a => ([...a, text]));
}
return ''
});
}
}
document.getElementById('textbox').addEventListener('keyup', listener);
console.log('Event registered')
return () => {
document.getElementById('textbox').removeEventListener('keyup', listener);
console.log('Event deregistered')
}
}, [])
return (
<div >
<input type="text" id="textbox" onChange={(e) => setText(e.target.value)} value={text} />
<button id="add" onClick={addToList}> Add</button>
<ul>
{
list.map((a, i) => <li key={i}>{a} <button type="button" onClick={() => deleteItem(i)}>Delete</button></li>)
}
</ul>
</div>
);
}
export default App;
I think it's because of this in the useEffect in your code:
setText(text => {
if (text !== '') {
console.log("updating")
setList(a => ([...a, text]));
}
return ''
});
Change it to the following:
useEffect(() => {
console.log("Reloaded");
const listener = (e) => {
if (e.code === "Enter") {
console.log("event triggered");
if (text !== "") {
console.log("updating");
setList((a) => [...a, text]);
}
return "";
}
};
document.getElementById("textbox").addEventListener("keydown", listener);
console.log("Event registered");
return () => {
document
.getElementById("textbox")
.removeEventListener("keydown", listener);
console.log("Event deregistered");
};
}, [text]);
Or,
useEffect(() => {
console.log("Reloaded");
const listener = (e) => {
if (e.code === "Enter") {
console.log("event triggered");
if (e.target.value !== "") {
console.log("updating");
setList((a) => [...a, e.target.value]);
}
e.target.value = "";
return "";
}
};
document.getElementById("textbox").addEventListener("keydown", listener);
console.log("Event registered");
return () => {
document
.getElementById("textbox")
.removeEventListener("keydown", listener);
console.log("Event deregistered");
};
});
OR,
This one uses onKeyPress event and only runs useEffect once when the component is mounted
import React, { useState, useEffect } from "react";
const App = () => {
const [text, setText] = useState("");
const [list, setList] = useState([]);
const addToList = () => {
if (text !== "") {
setList([...list, text]);
setText("");
}
};
const deleteItem = (index) => {
const deletedList = list.filter((_, i) => i !== index);
setList(deletedList);
};
const listener = (e) => {
e.stopPropagation();
if (e.key === "Enter") {
console.log("event triggered");
if (text !== "") {
console.log("updating");
setList((a) => [...a, text]);
}
setText("");
return "";
}
};
// not needed, only here to show it only runs once
useEffect(() => {
console.log("Reloaded");
}, []);
return (
<div>
<input
type="text"
id="textbox"
onChange={(e) => setText(e.target.value)}
onKeyPress={listener}
value={text}
/>
<button id="add" onClick={addToList}>
{" "}
Add
</button>
<ul>
{list.map((a, i) => (
<li key={i}>
{a}{" "}
<button type="button" onClick={() => deleteItem(i)}>
Delete
</button>
</li>
))}
</ul>
</div>
);
};
export default App;
https://codesandbox.io/s/winter-dust-r3ylc?file=/src/App.js

Convert Class component into a handleChange Hook

I'm wanting to convert this class component into a handleChange event that works in a function component. I'm not entirely sure that's possible, it may have to be a hook. I can't quite get the syntax right.
class CheckboxForm extends Component {
constructor(props) {
super(props);
this.state = { value: [] };
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const input = event.target.value;
this.setState(
{
value: this.state.value.includes(input)
? this.state.value.filter((item) => item !== input)
: [...this.state.value, input],
},
() => {
console.log("CheckBox: ", this.state.value);
}
);
}
My attempt:
export const CheckboxHook = (props) => {
const [value, setValue] = useState([]);
const handleCheckbox = (event) => {
const input = event.target.value;
setValue(
value.include(input)
? value.filter((item) => item !== input)
: [value, input],
() => {
console.log("Checkbox: ", value);
}
);
};
};
Seems like you're trying to do something like this? You don't need any custom hooks for it - just useState and useEffect.
const CheckboxForm = (props) => {
const [checkedItems, setCheckedItems] = useState([]);
useEffect(() => {
// equivalent to passing a callback to `this.setState` in class component
console.log("Checked: ", checkedItems);
}, [checkedItems])
const handleCheckbox = (event) => {
const { value } = event.target;
// setting with hooks must use a callback
// if you need access to the previous value
setCheckedItems(items =>
items.includes(value)
? items.filter(item => item !== value)
: [...items, value]
);
};
return <form>
<label>a <input value="a" type="checkbox" onChange={handleCheckbox} /></label>
<label>b <input value="b" type="checkbox" onChange={handleCheckbox} /></label>
<label>c <input value="c" type="checkbox" onChange={handleCheckbox} /></label>
</form>
};
You can use a custom hook to wrap your handler logic and use it like:
const useHandleChange = () => {
const [value, setValue] = React.useState([]);
const handleChange = (event) => {
const input = event.target.value;
setValue(
{
value: value.includes(input)
? value.filter((item) => item !== input)
: [...value, input],
},
() => {
console.log("CheckBox: ", value);
}
);
};
return { value, handleChange };
};
// Usage
const { value, handleChange } = useHandleChange();
// In the render
<Input onChange={handleChange} value={value} />

Multiple functions in an onChange? Answers I've seen aren't working

I'm trying to do multiple things when an onChange event happens. I've seen some other answers, but all of them are giving the error "Expected an assignment or function call and instead saw an expression".
import React, {useState, useEffect} from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
},
[value, delay],
);
return debouncedValue;
}
function App() {
const [cardNames, setCardName] = useState([]);
const [searchInput, setSearchInput] = useState("pikachu");
const debouncedInput = useDebounce(searchInput, 1000)
const rezCards = async () => {
const rez = await fetch('https://api.pokemontcg.io/v1/cards?name='+searchInput+'')
const json = await rez.json()
setCardName(json.cards)
}
useEffect(() => {
rezCards()
},[debouncedInput])
return <aside>
<p>Search Term: {searchInput}</p>
<form>
<input id="search-field" type="text" value={searchInput} onChange = {
searchCard => setSearchInput(searchCard.target.value)
}></input>
</form>
<hr />
<ul>
{cardNames
.map(
(cardName) => {
return <li key={cardName.id}><img src={cardName.imageUrl} /><span className="poke-cardname">{cardName.name}</span></li>
}
)}
</ul>
</aside>
}
export default App
The line is the onChange in the #search-field input. I've tried this:
<input id="search-field" type="text" value={searchInput} onChange = {
() => { searchCard => setSearchInput(searchCard.target.value); console.log("Test"); }
}></input>
(The console.log could be anything, such as another function. It doesn't matter, the error is always the same.)
Looks like you were missing curly brackets around the multiple functions you want triggered. Without the brackets you are implying a return value (the first function) only.
<input onChange={
(searchCard) => {
setSearchInput(searchCard.target.value);
console.log("Test");
}
}></input>
searchCard => setSearchInput(searchCard.target.value); console.log("Test");
---Problem is Here.
<input id="search-field" type="text" value={searchInput} onChange = {
(searchCard) => {
setSearchInput(searchCard.target.value);
console.log("Test");
}
}></input>

Resources