I am building a to-do list in react and when adding a new task to the list, the input value is not being cleared from the form after submit, I'm trying to achieve this using setInput('');
const Form = (props) => {
const [input, setInput] = useState('');
const handleChange = e => {
setInput(e.target.value);
}
const handleSubmit = e => {
e.preventDefault();
const newTask = {
id: uuidv4(),
text: input,
completed: false
};
props.onSubmit(newTask);
setInput('');
}
return (
<form
className='task-form'
>
<input
className='task-input'
type='text'
placeholder='Enter a task'
name='text'
onChange={handleChange}
/>
<button className='task-btn' onClick={handleSubmit}>Add Task</button>
</form>
)
}
export default Form
You aren't using the input state anywhere except when creating the newTask object. You need to pass it as a prop to the <input> component to make it fully controlled and for calls to the state setter to result in changes to the DOM.
<input
className='task-input'
type='text'
placeholder='Enter a task'
name='text'
onChange={handleChange}
/>
should be
<input
className='task-input'
type='text'
placeholder='Enter a task'
name='text'
onChange={handleChange}
value={input}
/>
Related
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;
I have this simple example using useDebouncedCallback from use-debounce. When i write to input it remains the same with no value. What iam doing wrong?
const SignupForm = () => {
const formik = useFormik({
initialValues: {
firstName: "",
},
});
const debounced = useDebouncedCallback(
// function
(event) => {
formik.handleChange(event);
},
// delay in ms
1000
);
return (
<form onSubmit={formik.handleSubmit}>
<label htmlFor="firstName">First Name</label>
<input
id="firstName"
name="firstName"
type="text"
onBlur={formik.handleBlur}
onChange={(e) => {
e.persist();
debounced(e);
}}
value={formik.values.firstName}
/>
<button type="submit">Submit</button>
</form>
);
};
It's not working because you have a controlled component (due to value={formik.values.firstName}).
When formik.handleChange is called (in an debounced way), e.target.value is empty because React keeps the input field in sync with formik.values.firstName which remains empty (its initial value is firstName: "").
To make it work, you can use the formik setFieldValue and pass the input name and value to the debounced function like this :
const debounced = useDebouncedCallback(
(field, value) => formik.setFieldValue(field, value),
1000
);
...
<input
id="firstName"
name="firstName"
type="text"
onBlur={formik.handleBlur}
onChange={(e) => {
const { name, value } = e.target;
debounced(name, value);
}}
value={formik.values.firstName}
/>
Here is a stackblitz example
Here I set the initial state and the target state using react hooks. In the from component I used Semantic UI React from to create the form used value in input to set some default values that has been already entered through the form. Used onChange to edit the values but it is not working.But the onChange is working perfectly.
Parent Component
const ParentPane = (props) => {
const dispatch = useDispatch();
const [appName, setAppName] = useState([{ appName: props?.rowEditData?.appName }]);
const [pattern, setPattern] = useState("");
const [desc, setDesc] = useState("");
const [parentData, setParentData] = useState({
appName: "",
pattern: "",
desc: ""
});
Form Component
<Form size="large" inverted className="parentStyle">
<div>
<Form.Input
fluid
id="app"
name="appName"
label="Application Name"
value={props?.rowEditData.appName}
onChange={(event) => setAppName(event.target.value)}
/>
<Form.Input
fluid
id="path"
name="pattern"
label="Request Path"
placeholder="eg: /home"
value={props?.rowEditData.pattern}
onChange={(event) => setPattern(event.target.value)}
/>
<Form.Input
fluid
path="des"
name="desc"
label="Description"
value={props?.rowEditData.description}
onChange={(event) => setDesc(event.target.value)}
/>
<Button
className="submitButton"
onClick={(event) => saveOrUpdateMock(event, "UPDATE")}
style={style.labelStyle}
>
Save
</Button>
</div>
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)} />
Is there another way of getting/setting the values from the dom that is less expensive than useRef()? Is useRef() to be used lightly as the docs suggest?
import React, { useRef, useEffect } from 'react';
const Join: React.FC = () => {
const fullName = useRef<HTMLInputElement>(null);
const email = useRef<HTMLInputElement>(null);
const password = useRef<HTMLInputElement>(null);
const myForm = useRef<HTMLFormElement>(null);
useEffect(() => {
if (myForm.current) myForm.current.reset();
if (fullName.current) fullName.current.focus();
}, []);
return (
<div>
<form ref={myForm}>
<input type='text' ref={fullName} />
<input type='text' ref={email} />
<input type='text' ref={password} />
</form>
</div>
)
}
When the component loads I want to clear the form and focus the
fullName input
You don't need refs for that
I want to clear the form
Make your inputs controlled
Declare an empty string as initial value
const Component = () =>{
const [state, setState] = useState({
email : '',
password : ''
})
const onChange = ({ target: { value, name } }) =>{
setState(prev => ({
...prev,
[name] : value
}))
}
const { email, password } = state
return(
<>
<input value={email} onChange={onChange} id='email'/>
<input value={password} onChange={onChange} id='password' />
</>
)
}
Automatically focus a given input
Just use autofocus for that
<input autofocus/>