I have questionand I hope you can help me to find right answer.
I have the following code.
import Slider from '#material-ui/core/Slider';
export default function RangeSlider({setValue1}) {
const location = useLocation();
const classes = useStyles();
const [value, setValue] = useState(['', 150000]);
const handleChange = (event, newValue) => {
setValue(newValue);
setValue1(value);
};
useEffect(() => {
setValue(['', 150000]);
setValue1(value);
}, [location.search]);
return (
<div className={`filter-slider-wrapper ${classes.root} `}>
<p>
<input
type="number"
className="slider-input"
onChange={({target}) => {
if (target.value.length > 7) {
return false;
}
setValue([+target.value, value[1]]);
setValue1(value);
}}
value={value[0]}
/>
</p>
<Slider value={value} onChange={handleChange} min={1} max={150000} />
<p>
<input
type="number"
className="slider-input"
onChange={({target}) => {
if (target.value.length > 7) {
return false;
}
setValue([value[0], +target.value]);
setValue1(value);
}}
value={value[1]}
/>
</p>
</div>
);
}
When I typeing something in input and after that I delleting form input everything was deleted, but after that "0" number was appeared and it was not deleteable, how can I avoid that case?
Thank you everybody.
I alredy resolved this problem.
I just deleted "+" symbol in setValue([+target.value, value[1]]);
Now the correct way to avoid "0" number to write without "+" symbol
Right way is: setValue([target.value, value[1]]);
Related
I have a list of users and I want to display in another component on the same page the user data in input fields for every user that I click on.
When no user is selected, I want the component to just render some text and a button to add a user. When the button is clicked the component renders the form with empty input fields so that we can add a new user.
I tried the following, but it's just showing the data for the first one I click on. It's not updating.
The main page:
const index = (props) => {
const [selectedUser, setSelectedUser] = useState(null);
const [state, setState] = useState("Index");
const onChange = (item) => {
setState("Edit");
setSelectedUser(item);
};
const onClick = (e, item) => {
if (e.type === "click" && e.clientX !== 0 && e.clientY !== 0) {
onChange(item);
} else {
console.log('prevented "onClick" on keypress');
}
};
const renderComponent = () => {
switch (state) {
case "Index":
return (
<>
<div className="btn" onClick={(e) => setState("Edit")}>
+ New Staff
</div>
<img src="/storage/illustrations/collaboration.svg" />
</>
);
case "Edit":
return (
<div>
<StaffForm profile={selectedUser} />
</div>
);
}
};
return (
<>
<div>
<div>
<h1>Staff</h1>
</div>
<div>
<div>
{profiles.map((item, index) => {
return (
<div key={index} onClick={(e) => onClick(e, item)}>
<input
type={"radio"}
name={"staff"}
checked={state === item}
onChange={(e) => onChange(item)}
/>
<span>{item.user.name}</span>
</div>
);
})}
</div>
<div>{renderComponent()}</div>
</div>
</div>
</>
);
};
The Staff Form Component:
const StaffForm = ({ profile }) => {
const { data, setData, post, processing, errors, reset } = useForm({
email: profile ? profile.user.email : "",
name: profile ? profile.user.name : "",
phone_number: profile ? profile.user.phone_number : "",
avatar: profile ? profile.user.avatar : "",
});
const [file, setFile] = useState(data.avatar);
const handleImageUpload = (e) => {
setFile(URL.createObjectURL(e.target.files[0]));
setData("avatar", e.target.files[0]);
};
const onHandleChange = (event) => {
setData(
event.target.name,
event.target.type === "checkbox"
? event.target.checked
: event.target.value
);
};
return (
<div>
<ImageUpload
name={data.name}
file={file}
handleImageUpload={handleImageUpload}
/>
<TextInput
type="text"
name="name"
value={data.name}
autoComplete="name"
isFocused={true}
onChange={onHandleChange}
placeholder={t("Name")}
required
/>
<TextInput
type="text"
name="phone_number"
value={data.phone_number}
autoComplete="phone_number"
placeholder={t("Phone Number")}
onChange={onHandleChange}
required
/>
<TextInput
type="email"
name="email"
value={data.email}
autoComplete="email"
onChange={onHandleChange}
placeholder={t("Email")}
required
/>
</div>
);
};
First of all something you should avoid is the renderComponent() call.Check here the first mistake mentioned in this video. This will most likely fix your problem but even if it doesn't the video explains why it should not be used.
Something else that caught my eye(possibly unrelated to your question but good to know) is the onChange function. When two pieces of state change together it is a potential source of problems, check out this article on when to use the useReducer hook.
Also be careful with naming React Components, they need to be capital case, this question contains appropriate answers explaining it.
(To only solve your problem stick to no.1 of this list, there are some improvements i'd do here overall for code quality and beauty, msg me for more details)
In React, I want that if something is typed into a form, another form is immediately set to focus.
For example:
<html>
<head>
<title>React App</title>
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone#6.15.0/babel.min.js"></script>
</head>
<body>
<div id="mydiv"></div>
<script type="text/babel">
//Now its basically like React
function Inputs() {
const [otp, setOtp] = React.useState([]); //Here are stored the Values of the numbers
const itemsRef = React.useRef([]); //Here are stored the refs of the numbers
const setOtpfunction = (e, i) => {
const key = parseInt(e.key); //I first parse the key that i got from onKeyPress to a number
if (key) {
let OTP = [...otp];
OTP[i] = key;
setOtp(OTP); //Then i update the numbers
if (i < 5) {
const nextForm = i + 1; //Here i say that the next item schould be the item with the index + 1
ref[i].focus(); //And here i set the focus. ref.current.focus() wouldnt work in this case
}
}
};
return (
<div className="space-x-2">
<input
placeholder="first input"
type="number"
className="otpNumber"
onChange={(e) => (e = null)} //that there is no error because there is no onChange function
required
onKeyPress={(e) => setOtpfunction(e, 0)}
ref={(ref) => (itemsRef[0] = ref)}
autoFocus
value={otp[0] || ""}
/>
<input
placeholder="second input"
type="number"
className="otpNumber"
onChange={(e) => (e = null)} //that there is no error because there is no onChange function
onKeyPress={(e) => setOtpfunction(e, 1)}
ref={(ref) => (itemsRef[1] = ref)}
value={otp[1] || ""}
/>
<input
placeholder="third input"
type="number"
className="otpNumber"
onChange={(e) => (e = null)} //that there is no error because there is no onChange function
onKeyPress={(e) => setOtpfunction(e, 2)}
ref={(ref) => (itemsRef[2] = ref)}
value={otp[2] || ""}
/>
<p>Now you can type in 1 number in the inputs and they are stored in UseState. But i want that if in the first input, one number is entered, it focuses the second input and so on</p>
</div>
);
}
class Render extends React.Component {
render() {
return (
<div>
<Inputs />
</div>
);
}
}
ReactDOM.render(<Render />, document.getElementById("mydiv"));
</script>
</body>
</html>
The console's error was only a warning that you should compile your scripts for production. That's why I hid it.
I also tested it with document.getElemntbyId, which also didn´t work.
I hope you can understand what I mean. Thanks for any answers that can be provided!
I would rather make a single Input component, that notifies the parent when the user types something and updates the state accordingly:
const OTP_LENGTH = 4;
export default function OTPForm() {
const [index, setIndex] = useState(0);
function done() {
if (index === OTP_LENGTH - 1) {
console.log("All inputs filled");
return;
}
setIndex(index + 1);
}
return (
<form>
{Array.from({ length: OTP_LENGTH }, (_, i) => (
<Input key={i} isCurrent={i === index} done={done} />
))}
</form>
);
}
function Input({ isCurrent, done }) {
const ref = useRef(null);
useEffect(() => {
if (isCurrent && ref.current) {
ref.current.focus();
}
}, [isCurrent]);
function onInput(event) {
if (event.target.value.length > 1) {
event.preventDefault();
return;
}
if (event.target.value.length === 1) {
done();
}
}
return <input ref={ref} onInput={onInput} type="number"/>;
}
Codesandbox
Up to you to expose proper onError / onSubmit handlers from the parent component.
So I have a submit form where the user needs to create a task by typing in a task name. I want it to be empty at the beginning and have a placeholder of "you must enter a task" when the user click add without entering anything. Now I can achieve it to display the placeholder but it's either always there or I encounter unreachable code. I know how to clean the submission & return to the add function, just need to be able to display the placeholder conditionally. Here's what my code looks like atm:
import { useState } from "react";
export default function Todos() {
const [todos, setTodos] = useState([{ text: "hey" }]);
const [todoText, setTodoText] = useState("");
const [isEmpty, setEmpty] = useState("false");
const addTodo = (e) => {
e.preventDefault();
if (todoText){
setTodos([...todos, { text: todoText }]);
setTodoText("");
} else {
setEmpty(true)
setTodoText("");
return
}
}
return (
<div>
{todos.map((todo, index) => (
<div key={index}>
<input type="checkbox" />
<label>{todo.text}</label>
</div>
))}
<br />
<form onSubmit={addTodo}>
<input
value={todoText}
onChange={(e) => setTodoText(e.target.value)}
type="text"
></input>
<button type="submit">Add</button>
{isEmpty &&<span style={{ color: "red" }}>Enter a task</span>}
</form>
</div>
);
}
I could change your code with the following:
You need to initialize isEmpty by false instead of string "false".
And you can use this flag on showing placeholder texts.
Note that I renamed isEmpty by showError.
import { useState } from "react";
export default function Todos() {
const [todos, setTodos] = useState([{text: "hey"}]);
const [todoText, setTodoText] = useState("");
const [showError, setShowError] = useState(false);
// #ts-ignore
const addTodo = (e) => {
e.preventDefault();
if (todoText) {
setTodos([...todos, {text: todoText}]);
setTodoText("");
setShowError(false);
} else {
setTodoText("");
setShowError(true);
return
}
}
return (
<div>
{todos.map((todo, index) => (
<div key={index}>
<input type="checkbox"/>
<label>{todo.text}</label>
</div>
))}
<br/>
<form onSubmit={addTodo}>
<input
value={todoText}
onChange={(e) => setTodoText(e.target.value)}
type="text"
></input>
<button type="submit">Add</button>
{(showError && !todoText) && <span style={{color: "red"}}>Enter a task</span>}
</form>
</div>
);
}
I've coded a OTP component but it has two issues that I couldn't solve.
The first issue is, when I press Backspace I want to delete the input value and then go to the previous input field to delete it.
The second issue is, when I delete a input (i.e the 3rd box) my code makes me focus on the next input field.
How can I implement Backspace key press properly and go to the previous input field when a input is deleted?
Here's what I've done so far:
import React, { useState } from "react";
import css from '../components/css/OTP.css'
const Axios = require('axios')
const OTP = () => {
const [otp, setOtp] = useState(new Array(4).fill(""));
const handleChange = (element, index) => {
if (isNaN(element.value)) return false;
setOtp([...otp.map((d, idx) => (idx === index ? element.value : d))]);
//Focus next input
if (element.nextSibling) {
element.nextSibling.focus();
}
}
//eğer girilen OTP backendden gelen OTP ile aynıysa matchleştiğini göster ve kullanıcıyı verifike et daha sonra dashboarda aktar.
const checkOTP = async () =>{
try
{
const url = "http://localhost:5000/auth/otp"
const response = await Axios.post(url , {otp} , {withCredentials: true})
if(response.status!==200){
alert("Invalid OTP entry")
}
else{
alert("OTP successful!")
}
}
catch(err)
{
console.log(err)
}
}
return (
<>
<div className="otp-window">
<div className="otp-modal">
<div className="row">
<div className="info">
<p className="otp-info">Please enter the OTP sent to your email</p>
{otp.map((data, index) => {
return (
<input
className="otp-field"
type="text"
name="otp"
maxLength="1"
key={index}
value={data}
onChange={e => handleChange(e.target, index)}
onFocus={e => e.target.select()}
/>
);
})}
<p className="otp-entered">OTP : {otp.join("")}</p>
<p>
<button
className="clear-button"
onClick={e => setOtp([...otp.map(v => "")])}
>
Clear
</button>
<button
className="verify-button"
onClick={e =>
alert("Entered OTP is " + otp.join(""))
}
onClick={checkOTP}>
Verify OTP
</button>
</p>
</div>
</div>
</div>
</div>
</>
);
};
export default OTP;
You can use an input event and check the event.inputType property of the event object like this. You might have to change the onChange prop to onInput though.
if (event.inputType === 'deleteContentBackward' && event.target.value === '') {
// Focus on the previous field
}
I'm building a controlled form with dynamic fields.
The Parent component get data from a redux store and then set state with the values.
I don't want to make it with too much code lines so I turn the dynamic fields into a component.
States stay in the parent component and I use props to pass the handlechange function.
Parent :
function EditAbout(props) {
const [img, setImg] = useState("");
const [body, setBody] = useState(props.about.body);
const [instagram, setInstagram] = useState(props.about.links.instagram);
const [linkedin, setLinkedIn] = useState(props.about.links.linkedin);
const [press, setPress] = useState(props.about.press)
const handleSubmit = (e) => {
// Submit the change to redux
};
// set states with redux store
useEffect(() => {
setBody(props.about.body);
setInstagram(props.about.links.instagram);
setLinkedIn(props.about.links.linkedin);
setPress(props.about.press);
}, []);
const handleChangeChild = (e, index) => {
e.preventDefault();
let articles = press
const {value, name } = e.target
if (name === "title") {
articles[index].title = value;
} else {
articles[index].link = value;
}
setPress(articles)
console.log(articles[index])
}
return (
<Box>
<h1>CHANGE ABOUT ME</h1>
<Input
label="Image"
name="img"
type="file"
variant="outlined"
margin="normal"
onChange={(e) => setImg(e.target.files)}
/>
<Input
label="body"
value={body}
name="body"
onChange={(e) => setBody(e.target.value)}
variant="outlined"
multiline
rowsMax={12}
margin="normal"
/>
<Input
label="instagram"
value={instagram}
name="instagram"
variant="outlined"
margin="normal"
onChange={(e) => setInstagram(e.target.value)}
/>
<Input
label="Linkedin"
value={linkedin}
name="linkedin"
variant="outlined"
margin="normal"
onChange={(e) => setLinkedIn(e.target.value)}
/>
<Child press={press} onChange={handleChangeChild} />
{props.loading ? (
<CircularProgress color="black" />
) : (
<Button onClick={handleSubmit} variant="contained">
Send
</Button>
)}
</Box>
);
}
Child :
function Child(props) {
const { press, onChange } = props;
const inputsMarkup = () =>
press.map((article, index) => (
<div key={`press${index}`} style={{ display: "flex" }}>
<input
name="title"
value={press[index].title}
onChange={(e) => onChange(e, index)}
/>
<input
name="link"
value={press[index].link}
onChange={(e) => onChange(e, index)}
/>
<button>Delete</button>
</div>
));
return (
<div>
<h1>Press :</h1>
{inputsMarkup()}
</div>
);
}
Everything is fine when I'm typing in the Parent inputs. But when I'm using Child fields state update for one character but come back at its previous state right after.
It also doesn't display the character change. I can only see it in the console.
Thanks you in advance for your help
The problem is that you're mutating the state directly. When you create the articles variable (let articles = press) you don't actually create a copy and articles doesn't actually contain the value. It's only a reference to that value, which points to the object’s location in memory.
So when you update articles[index].title in your handleChangeChild function, you're actually changing the press state too. You might think that's fine, but without calling setPress() React will not be aware of the change. So, although the state value is changed, you won't see it because React won't re-render it.
You need to create a copy of the press array using .map() and create a copy of the updated array element. You can find the updated handleChangeChild() below:
const handleChangeChild = (e, index) => {
e.preventDefault();
const { value, name } = e.target;
setPress(
// .map() returns a new array
press.map((item, i) => {
// if the current item is not the one we need to update, just return it
if (i !== index) {
return item;
}
// create a new object by copying the item
const updatedItem = {
...item,
};
// we can safely update the properties now it won't affect the state
if (name === 'title') {
updatedItem.title = value;
} else {
updatedItem.link = value;
}
return updatedItem;
}),
);
};