Clear value into input using useState or useRef (React) - reactjs

I got hook:
const [myName, setMyName] = useState("");
const myNameRef = useRef();
Then I have form:
<form onSubmit={(e) => addVist(e, user.id)}>
<input type="text" name="myName" ref={myNameRef} onChange={e => setMyName(e.target.value)} />
<input type="submit" value="Run" />
</form>
And method:
const addVist = (e, userId) => {
e.preventDefault();
console.log(myName)
//some code....
//clear value form
setMyName("");
//setMyName('');
//setMyName(e.target.value = "");
//myNameRef.current.value("");
}
But the setMyName("") is not working - still I see value inside input.

You missed binding myName as value attribute of the input tag.
<input type="text" name="myName" value={myName} ref={myNameRef} onChange={e => setMyName(e.target.value)} />

Here is a complete example of clearing input using state OR a reference:
export default function App() {
const [value, setValue] = useState("");
const inputRef = useRef();
return (
<>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<input ref={inputRef} />
<button
onClick={() => {
setValue("");
inputRef.current.value = "";
}}
>
Clear
</button>
</>
);
}
Refer to Controlled VS Uncontrolled components.

On your text input you should pass myName into the value attribute like so
<input type="text" name="myName" value={myName} ref={myNameRef} onChange={e => setMyName(e.target.value)} />

You forgot to add myName as value.
<input type="text" name="myName" value={myName} ref={myNameRef} onChange={e => setMyName(e.target.value)} />

Related

React keep focus on re-rendering input

I am building an e-commerce web app, and have run into a problem making an array of inputs. Inside DescHTML I am trying to handle onChange event, but on each update the focus on the element is lost, making it very annoying to type. Does anyone have any solution or different aproach to this whole problem? Thanks in advance.
import {useState} from 'react'
export default function UploadForm() {
const [name, setName] = useState('')
const [category, setCategory] = useState('')
const [film, setFilm] = useState('')
const [price, setPrice] = useState('')
const [descArr, setDescArr] = useState([''])
function DescHTML() {
return (
<div>
{ descArr.map((item, i) =>
<input type="text" placeholder="TEST" key={i} value={item} onChange={(e) => {
e.preventDefault()
const newArr = [...descArr]
newArr[i] = e.target.value
setDescArr(newArr)
} } /> )}
</div>
)
}
console.log(descArr)
return (
<div>
<form method="POST" action="http://localhost:3500/api/upload" encType='multipart/form-data'>
<input type="file" name="image" />
<input type="text" value={name} name="name" onChange={(e) => setName(e.target.value)} placeholder="Product name" />
<input type="text" value={category} name="category" onChange={(e) => setCategory(e.target.value)} placeholder="Category" />
<input type="text" value={film} name="film" onChange={(e) => setFilm(e.target.value)} placeholder="Film" />
<input type="text" value={price} name="price" onChange={(e) => setPrice(e.target.value)} placeholder="Price" />
<DescHTML />
<hr />
{/*<input type="submit" value="Submit" />*/}
</form>
<button onClick={() => setDescArr([...descArr, ''])}>+</button>
</div>
)
}
Move your descArr and button inside DescHTML check below code you can check more info here.
function DescHTML() {
const [descArr, setDescArr] = useState([""]);
return (
<div>
{descArr.map((item, i) => (
<input
key={i}
type="text"
placeholder="TEST"
value={item}
onChange={(e) => {
e.preventDefault();
const newArr = [...descArr];
newArr[i] = e.target.value;
setDescArr(newArr);
}}
/>
))}
<br />
<button onClick={() => setDescArr([...descArr, ""])}>+</button>
</div>
);
}
First of all - move DescHTML out of UploadForm:
function DescHTML({descArr, setDescArr}) {
return (
<div>
{descArr.map((item, i) =>
<input type="text" placeholder="TEST" key={i} value={item} onChange={(e) => {
e.preventDefault()
const newArr = [...descArr]
newArr[i] = e.target.value
setDescArr(newArr)
}}/>)}
</div>
)
}
and use it in <UploadForm> like this
<DescHTML descArr={descArr} setDescArr={setDescArr}/>
The other problem is the + button which steals the focus on click. Adding onMouseDown handler will fix this.
<button onMouseDown={(e) => e.preventDefault()} onClick={() => setDescArr([...descArr, ''])}>+</button>

How to pass state props to another component

I want to pass a state variable to another component but i don't get it what i'm doing wrong.
I have a component for radio inputs and I need to pass that taskLabel to Form component.
path is components/inputs/index.js
const TaskLabel = (props) => {
const [taskLabel, setTaskLabel] = useState('');
return (
<div label={props.taskLabel} >
<input
type='radio'
name='label'
value='urgent'
onChange={(e) => setTaskLabel(e.target.value)}
/>
<input
type='radio'
name='label'
value='not-urgent'
onChange={(e) => setTaskLabel(e.target.value)}
/>
</div>
);
};
i want to receive the taskLabel value, to use it in submitHandler function.
components/form/index.js
const Form = ({taskLabel}) => {
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Text here'
className='form-input'
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel taskLabel={taskLabel} />
</form>
)
}
This is what i tried, to pass taskLabel props from label={taskLabel}.
You need to move your state, to Form component, like this:
const [labelProp, setLabelProp] = useState("");
Your TaskLabel component should be
<TaskLabel label={{ labelProp, setLabelProp }} />
That means, you send label, as a prop to TaskLabel component.
In TaskLabel component you need to recive the prosp, by passing to component {label}.
Next, for every input use onChange={(e) => label.setLabelProp(e.target.value)}.
Edited sandbox => https://codesandbox.io/s/laughing-proskuriakova-dhi568?file=/src/components/task-label/index.js
Generally speaking, the concept is "data-down, actions-up". So if you want to pass data down to a lower-level component, you just pass a prop. If you want to update a value from a lower-level component to a higher-level component, you could pass a setter function down as a prop and call that.
Note I just call it taskLevelProp for a little more clarity. You should probably use a better name.
TaskLabel (lower level component)
/* It looks like TaskLabelProp gets passed in from something else.
Otherwise, you would use useState to get your setter function */
const TaskLabel = ({taskLabelProp, setTaskLabelProp}) => {
return (
<div label={props.taskLabel} >
<input
type='radio'
name='label'
value='urgent'
onChange={(e) => setTaskLabelProp(e.target.value)}
/>
<input
type='radio'
name='label'
value='not-urgent'
onChange={(e) => setTaskLabelProp(e.target.value)}
/>
</div>
);
};
Form (higher level component)
const Form = () => {
const [taskLabelProp, setTaskLabelProp] = useState('');
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Text here'
className='form-input'
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel taskLabel={taskLabelProp, setTaskLabelProp} />
</form>
)
}
Let me know if this answers your question.
EDIT: Made Form use useState. Based off your code, I was assuming you were using useState at the app level.
function App() {
const [task, setTask] = useState("");
const [taskLabelProp, setTaskLabelProp] = useState("");
const handleChange = (e) => {
setTaskLabelProp(e.target.value);
};
const submitHandler = (e) => {
e.preventDefault();
};
return (
<div className="App">
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="Text here"
className="form-input"
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel
onChange={handleChange}
taskLabelProp={taskLabelProp}
taskLabel="select"
/>
<button type="submit">fs</button>
</form>
</div>
);
}
export default App;
const TaskLabel = ({ taskLabel, onChange, taskLabelProp }) => {
return (
<div label={taskLabel}>
<input
type="radio"
name="label"
value="urgent"
onChange={(e) => onChange(e)}
checked={taskLabelProp === "urgent"}
/>
urgent
<input
type="radio"
name="label"
value="not-urgent"
onChange={(e) => onChange(e)}
checked={taskLabelProp === "not-urgent"}
/>
not-urgent
</div>
);
};
export default TaskLabel;

Can't type in textarea when displayed

import React, { useState } from 'react'
const FormBox = () => {
const [name, setName] = useState("")
const [textArea, setTextArea] = useState('')
const handleSumbit = (e) =>{
e.preventDefault();
console.log(name)
}
return (
<form onSubmit={handleSumbit}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<textarea value={textArea} onChange={handleSumbit}></textarea>
<input type="submit" />
</form>
)
}
When the text box is displayed I cannot type in it.
What am I doing wrong...?
import React, { useState } from 'react'
const FormBox = () => {
const [name, setName] = useState('')
const [textArea, setTextArea] = useState('')
const handleSumbit = (e) =>{
e.preventDefault();
console.log(name)
}
return (
<form onSubmit={handleSumbit}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<textarea value={textArea} onChange={(e) => setTextArea(e.target.value)}></textarea>
<input type="submit" />
</form>
)
}
The textarea is a controlled input - if you are going to tie the value of the <textarea> to the textArea state variable, you need to update that state variable whenever the user changes the input.
Shouldn't the onChange={handleSumbit} of the textarea be
onChange={(e) => setTextArea(e.target.value)}
import React, { useState } from 'react'
const FormBox = () => {
const [name, setName] = useState("")
const [textArea, setTextArea] = useState('')
const handleSumbit = (e) =>{
e.preventDefault();
console.log(name)
}
return (
<form onSubmit={handleSumbit}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setTextArea(e.target.value)}
/>
</label>
<textarea value={textArea} onChange={handleSumbit}></textarea>
<input type="submit" />
</form>
)
}
Firstly
You implicitly set the value of your text area using the textArea variable which has an initial state of "" (an empty string).
React automatically refreshes the real DOM from the virtual DOM after every change in state. But the value of your textArea variable doesn't change with this event, so you have to update the state when a value is entered like this:
onChange={(e) => setTextArea(e.target.value)}
After reading your code, I guessed what you wanted to achieve is to prevent the submit button from submitting the form by default and instead logs the name on the console.
I believe this is the code you wanted to achieve:
import React, { useState } from 'react'
const FormBox = () => {
const [name, setName] = useState('')
const [textArea, setTextArea] = useState('')
const handleSumbit = (e) =>{
e.preventDefault();
console.log(name)
}
return (
<form onSubmit={handleSumbit}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<textarea value={textArea} onChange={(e) => setTextArea(e.target.value)}></textarea>
<input type="submit" />
</form>
)
You can't type here the event not written by you properly, so, you state textarea not updated yet. just needed to change one as a similar name textbox. just replace
<textarea value={textArea} onChange={handleSumbit}></textarea>
to
<textarea value={textArea} onChange={(e) => setTextArea(e.target.value)}></textarea>
Full code here edited,
import React, { useState } from 'react'
const FormBox = () => {
const [name, setName] = useState('')
const [textArea, setTextArea] = useState('')
const handleSumbit = (e) =>{
e.preventDefault();
// console here form data
}
return (
<form onSubmit={(e)=>handleSumbit(e)}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<textarea value={textArea} onChange={(e) => setTextArea(e.target.value)}></textarea>
<input type="submit" value="Submit now!" />
</form>
)
I Hope, work fine using this code
Thanks

Data Fetching with React using useEffect

What I am trying to do is when the user click the edit button, this will send him to a new page where he can modify the info he already entered. The problem I am facing is that the new page is not showing the data previously entered, so that the user can make his changes. Also, the submit button to send those changes is not working. These are the errors I am getting: src\components\RestaurantList.jsx
Line 25:8: React Hook useEffect has a missing dependency: 'setRestaurants'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Line 31:19: 'response' is assigned a value but never used no-unused-vars
src\components\UpdateRestaurant.jsx
Line 9:12: 'restaurants' is assigned a value but never used no-unused-vars
Line 38:8: React Hook useEffect has a missing dependency: 'code'. Either include it or remove the dependency array react-hooks/exhaustive-deps
My code for the component I am working on:
import React, {useState, useContext, useEffect} from 'react';
import { useHistory, useParams } from 'react-router-dom';
import RestaurantFinder from '../apis/RestaurantFinder';
import { RestaurantsContext } from '../context/RestaurantsContext';
const UpdateRestaurant = (props) => {
const {code} = useParams();
const {restaurants} = useContext(RestaurantsContext);
let history = useHistory();
const [name, setName] = useState("");
const [value, setValue] = useState ("");
const [strain, setStrain] = useState ("");
const [weight, setWeight] = useState ("");
const [authors, setAuthors] = useState ("");
const [number, setNumber] = useState ("");
const [page, setPage] = useState ("");
const [date, setDate] = useState ("");
useEffect(() => {
const fetchData = async () => {
const response = await RestaurantFinder.get(`/${code}`);
console.log(response.data.data);
setName(response.data.data.restaurant.name);
setValue(response.data.data.restaurant.value);
setStrain(response.data.data.restaurant.strain);
setWeight(response.data.data.restaurant.weight);
setAuthors(response.data.data.restaurant.authors);
setNumber(response.data.data.restaurant.number);
setPage(response.data.data.restaurant.page);
setDate(response.data.data.restaurant.date);
};
fetchData();
}, []);
const handleSubmit = async(e) => {
e.preventDefault();
const updatedRestaurant = await RestaurantFinder.put(`/${code}`, {
name,
value,
strain,
weight,
authors,
number,
page,
date,
});
console.log(updatedRestaurant);
history.push("/");
};
return (
<div>
<form action="">
<div className="form-group">
<label htmlFor="name">Name</label>
<input value={name} onChange={(e) => setName(e.target.value)} code="name" className="form-control" type="text" />
</div>
<div className="form-group">
<label htmlFor="Value">Value</label>
<input value={value} onChange={(e) => setValue(e.target.value)} code="value" className="form-control" type="float" />
</div>
<div className="form-group">
<label htmlFor="Strain">Strain</label>
<input value={strain} onChange={(e) => setStrain(e.target.value)} code="strain" className="form-control" type="text" />
</div>
<div className="form-group">
<label htmlFor="Weight">Weight</label>
<input value={weight} onChange={(e) => setWeight(e.target.value)} code="weight" className="form-control" type="float" />
</div>
<div className="form-group">
<label htmlFor="Author">Author</label>
<input value={authors} onChange={(e) => setAuthors(e.target.value)} code="authors" className="form-control" type="text" />
</div>
<div className="form-group">
<label htmlFor="Number">Number</label>
<input value={number} onChange={(e) => setNumber(e.target.value)} code="number" className="form-control" type="number" />
</div>
<div className="form-group">
<label htmlFor="Page">Page</label>
<input value={page} onChange={(e) => setPage(e.target.value)} code="page" className="form-control" type="number" />
</div>
<div className="form-group">
<label htmlFor="date">Date</label>
<input value={date} onChange={(e) => setDate(e.target.value)} code="date" className="form-control" type="number" />
</div>
<button onClick={handleSubmit} type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
)
}
export default UpdateRestaurant
for reusable code, it may be best to just do something like this.
This is probably not the answer, but I hope it helps you find out the answer.
const [data, setData ] = useState({restraunt.loaded:"false"});
useEffect(() => {
const fetch = async () => {
const response = await RestaurantFinder.get(`/${code}`);
console.log(response.data.data);
setData({...response.data.data, restraunt.loaded:"true"});
};
fetch();
},[Data.restraunt.loaded])
const {name, value , page, loaded } = Data.restaurant;
return (
<div><h1>{loaded}</h1>
</div>
)
If it shows loaded as false then you know it is because of the data not loading.

Reseting value of input element using React Hooks

I made a useInput hook that looks like this.
export function useInput({
type,
name,
placeholder,
initialValue,
helpText,
required,
onKeyUp,
errorMessage,
}: Props) {
const [value, setValue] = useState(initialValue)
const input =
type === 'textarea' ? (
<div className="form-group">
<label>{name}</label>
<span>{helpText}</span>
<textarea
name="email"
className="form-control"
required={required}
id={name}
value={value}
aria-describedby={name}
placeholder={placeholder}
onChange={(e) => setValue(e.target.value)}
/>
</div>
) : (
<div className="form-group">
<label>{name}</label>
<span>{helpText}</span>
<input
type={type}
name="email"
className="form-control"
id={name}
required={required}
value={value}
aria-describedby={name}
placeholder={placeholder}
onChange={(e) => setValue(e.target.value)}
onKeyUp={onKeyUp}
/>
<>{errorMessage}</>
</div>
)
return [value, input]
}
In my pages, I can simply use the hook, like below
const [description, descriptionInput] = useInput({
type: 'textarea',
name: 'Description',
helpText: <small className="muted"> (Optional)</small>,
placeholder: 'I found this interesting because...',
})
and inside the render function use them as {descriptionInput} and the value will be available in description.
However, I'm looking for ways to update the value programmatically - to be specific, to reset the value of inputs on form submission for example. All I have is a reference to the input, and the value itself. Is there a nice way to keep using the hook and reset the value at will?
You can simply expose a reset function from your hook which you can call to reset the state to either the initialValue or empty
export function useInput({
type,
name,
placeholder,
initialValue,
helpText,
required,
onKeyUp,
errorMessage,
}: Props) {
const [value, setValue] = useState(initialValue);
const resetInput = useCallback(() => {
setValue(initialValue || "" );
}, [])
const input =
type === 'textarea' ? (
<div className="form-group">
<label>{name}</label>
<span>{helpText}</span>
<textarea
name="email"
className="form-control"
required={required}
id={name}
value={value}
aria-describedby={name}
placeholder={placeholder}
onChange={(e) => setValue(e.target.value)}
/>
</div>
) : (
<div className="form-group">
<label>{name}</label>
<span>{helpText}</span>
<input
type={type}
name="email"
className="form-control"
id={name}
required={required}
value={value}
aria-describedby={name}
placeholder={placeholder}
onChange={(e) => setValue(e.target.value)}
onKeyUp={onKeyUp}
/>
<>{errorMessage}</>
</div>
)
return [value, input, resetInput]
}
and use it like
const [description, descriptionInput, resetDescriptionInput] = useInput({
type: 'textarea',
name: 'Description',
helpText: <small className="muted"> (Optional)</small>,
placeholder: 'I found this interesting because...',
})
P.S. However, since this hook is actually returning JSX content, you could write it as a component too and expose a function to be used by a ref with useImperativeHandle

Resources