I'm trying to send data from inputs to redax storage. Why does the dispatch(addChild(parent)) get an error?
Error: Invalid hook call. Hooks can only be called inside of the body of a function component
import {useDispatch} from "react-redux";
import { addChild} from "../../store/actions/actions";
const Form = () => {
const [parent, setParent] = useState([{name: "", age: ""}]);
const [childList, setChildList] = useState([{name: "", age: ""}])
const dispatch = useDispatch;
const handleChange = (e, index) => {
const { name, value } = e.target;
const child = [...childList];
child[index][name] = value;
setChildList(child)
}
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addChild(parent))
}
const addChildElement = ()=> {
setChildList( [...childList, {name: "", age: ""}]);
}
return (
<form className="form" onSubmit={handleSubmit}>
<div className="form__parent">
<div className="form-title">Персональные данные</div>
<div className="form-item">
<input className="form-input" type="text"
value={parent.name}
onChange={(e) => setParent({...parent, name: e.target.value})}
/>
<label className="form-label">Имя</label>
</div>
<div className="form-item">
<input className="form-input" type="number"
value={parent.age}
onChange={(e) => setParent({...parent, age: e.target.value})}
/>
<label className="form-label">Возраст</label>
</div>
</div>
<div className="form__child">
<div className="form__child-head">
<div className="form-title">Дети (макс.5)</div>
<button className="btn add-child-btn" onClick={addChildElement}>Добавить ребенка</button>
</div>
<ul className="form__child-content">
{
childList.map((value, id) => {
return (
<li className="child-list" key={id}>
<div className="form-item">
<input className="form-input" type="text"
name="name"
value={value.name}
onChange={e => handleChange(e, id)}
/>
<label className="form-label">Имя</label>
</div>
<div className="form-item">
<input className="form-input" type="number"
value={value.age}
name="age"
onChange={e => handleChange(e, id)}
/>
<label className="form-label">Возраст</label>
</div>
<button className="remove-list">Удалить</button>
</li>
);
})
}
</ul>
<input className="btn submit" type="submit" value="Сохранить" />
</div>
</form>
);
}
export default Form;`
This is the mistake you have made. call brackets() are missing in your useDispatch declaration.
it should be corrected like below
const dispatch = useDispatch();
Error is totally valid because now your dispatch is not the output of useDispatch. It's useDispatch itself and error is due to useDispatch hook is used inside handleSubmit.
Related
looking to use react hook form with useEffect to get changes in real time (as the user is filling out the form), is there a reason why useEffect isn't triggered here and if so is there a way to trigger it whenever the form data changes? example here is from https://remotestack.io/react-hook-form-set-update-form-values-with-useeffect-hook/
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
export default function SimpleForm() {
const { register, handleSubmit, reset, formState } = useForm();
const [student, initStudent] = useState(null);
useEffect(() => {
setTimeout(
() =>
initStudent({
name: "Little Johnny",
email: "lil#johnny.com",
grade: "3rd",
}),
1200
);
}, []);
useEffect(() => {
console.log("updating.,..");
reset(student);
}, [reset, student]);
function onFormSubmit(dataRes) {
console.log(dataRes);
return false;
}
return (
<div>
<h2 className="mb-3">
React Initiate Form Values in useEffect Hook Example
</h2>
{student && (
<form onSubmit={handleSubmit(onFormSubmit)}>
<div className="form-group mb-3">
<label>Name</label>
<input
type="text"
name="name"
{...register("name")}
className="form-control"
/>
</div>
<div className="form-group mb-3">
<label>Email</label>
<input
type="email"
name="email"
{...register("email")}
className="form-control"
/>
</div>
<div className="form-group mb-3">
<label>Grade</label>
<input
type="text"
name="grade"
{...register("grade")}
className="form-control"
/>
</div>
<button type="submit" className="btn btn-dark">
Send
</button>
</form>
)}
{!student && (
<div className="text-center p-3">
<span className="spinner-border spinner-border-sm align-center"></span>
</div>
)}
</div>
);
}
You can use watch mode of react hook form to get every change.
const { register, handleSubmit, reset, formState, watch } = useForm();
useEffect(() => {
watch((value, { name, type }) => console.log(value, name, type));
}, [watch]);
Read more about watch mode form here
You need to trigger a state change whenever your input field value changes, and you do so using onClick event attribute like so:
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
export default function SimpleForm() {
const { register, handleSubmit, reset, formState } = useForm();
const [student, initStudent] = useState(null);
useEffect(() => {
setTimeout(
() =>
initStudent({
name: "Little Johnny",
email: "lil#johnny.com",
grade: "3rd",
}),
1200
);
}, []);
useEffect(() => {
console.log("updating.,..");
reset(student);
}, [reset, student]);
function onFormSubmit(dataRes) {
console.log(dataRes);
return false;
}
return (
<div>
<h2 className="mb-3">
React Initiate Form Values in useEffect Hook Example
</h2>
{student && (
<form onSubmit={handleSubmit(onFormSubmit)}>
<div className="form-group mb-3">
<label>Name</label>
<input
type="text"
name="name"
{...register("name")}
onClick={(e)=>initStudent({...student, name: e.target.value})}
className="form-control"
/>
</div>
<div className="form-group mb-3">
<label>Email</label>
<input
type="email"
name="email"
{...register("email")}
onClick={(e)=>initStudent({...student, email: e.target.value})}
className="form-control"
/>
</div>
<div className="form-group mb-3">
<label>Grade</label>
<input
type="text"
name="grade"
{...register("grade")}
onClick={(e)=>initStudent({...student, grade: e.target.value})}
className="form-control"
/>
</div>
<button type="submit" className="btn btn-dark">
Send
</button>
</form>
)}
{!student && (
<div className="text-center p-3">
<span className="spinner-border spinner-border-sm align-center"></span>
</div>
)}
</div>
);
}
I have the following code in my react app and I need to have empty input areas after submitting. Please assist me.
import { useRef } from 'react';
import './Contact.css';
import emailjs from 'emailjs-com'
import { useState, useEffect } from 'react';
const Contact = () => {
const formRef = useRef();
const [done, setDone] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
emailjs.sendForm(
'service_py6v3mm',
'template_db5q8nx',
formRef.current,
'mJDC1if10C25Z-TZC'
)
.then((result) => {
console.log(result.text);
setDone(true);
}, (error) => {
console.log(error.text);
});
}
return (
<div className="c">
<div className='c-wrapper'>
<div className='c-left'>
<h1 className='c-title'> Let's discuss!</h1>
<div className='c-info'>
<div className='c-info-item'>
<div className="c-info-item">
<img
src="./images/Phone.jpg"
alt=""
className="c-icon"
/>
+12345678 </div>
<div className="c-info-item">
<img className='c-icon'
src="./images/Email.png" alt='Email' />
messimacky#gmail.com
</div>
<div className="c-info-item">
<img className='c-icon'
src="./images/Location.jpeg"
alt=" " />
Addis Ababa, Wolo Sefer, Ethio-China Road, Ethiopia
</div>
</div>
</div>
</div>
<div className='c-right'>
<p className='c-desc'> <b> Get in touch!</b>
</p>
<form ref={formRef} onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='username' />
<input type='text' placeholder='Subject' name='user_subject' />
<input type='text' placeholder='Your email here... ' name='user_email' />
<textarea rows={5} placeholder='message' name='message' /> <br />
<button>Submit</button>
{done && <p> Thank you I will contact you soon!</p>}
</form>
</div>
</div>
</div>
)
}
export default Contact
You can bind every input value to a state and empty them when you submit it. Here I add an example for the username. You can multiply it and use it.
const [username, setUsername] = useState('Name');
const submitFunctionWhichDeletes = () => {
console.log(username);
setUsername('');
}
<input ... value={username} onChange={e => setUsername(e.target.value)} ... />
const compForm = ()=>{
const [formData,addFormData] = useState({
username:"",
subject:"",
email:"",
message:""
})
cosnt formSubmit =()=>{
// make api call
addFormData({
username:"",
subject:"",
email:"",
message:""
})
}
const formData = (e,filed)=>{
const temp = {...formData}
if (filed === "username"){
temp.username = e.target.value
}
else if(filed === "subject"){
temp.subject = e.target.value
}
else if(filed === "email"){
temp.email = e.target.value
}
else if(filed === "message"){
temp.message = e.target.value
}
addFormData(temp)
}
return(
<>
<input type='text' placeholder='Name' name='username'
value={formData.username} onChange={(e)=>formData(e,username)}/>
<input type='text' placeholder='Subject' name='user_subject'
value={formData.subject} onChange={(e)=>formData(e,subject)}/>
<input type='text' placeholder='Your email here... ' name='user_email'
value={formData.email} onChange={(e)=>formData(e,email)}/>
<textarea rows={5} placeholder='message' name='message'
value={formData.message} onChange={(e)=>formData(e,message)}/>
<button onClick = {(e)=>formSubmit()}>Submit</button>
<>
)
}
I have the following form, and I need that when submitting the form, its information is displayed in a new component.
Perhaps the issue of redirecting to the other component could be done by creating a route. But I don't know how said component obtains the information of the form
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import {useState, useRef} from 'React'
export default const FormX = () => {
const [formValues, setFormValues] = useState({
name: "",
priceUnitary: "",
size: "",
description: "",
});
const inputFileRef = useRef();
const handleChange = (event) => {
const { name, value } = event.target;
console.log(name, value);
setFormValues({ ...formValues, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formValues);
console.log(inputFileRef.current.files);
};
return (
<>
<form id="formu" onSubmit={handleSubmit} className="row">
<h1>FORM SEND</h1>
<div className="col-md-6">
<label>Name</label>
<input
placeholder="Text input"
name="name"
value={formValues.name}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Size</label>
<input
type="number"
placeholder="Text input"
name="size"
value={formValues.size}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Price Unitary</label>
<input
type="number"
placeholder="Text input"
name="priceUnitary"
value={formValues.priceUnitary}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Description</label>
<input
placeholder="Text input"
name="description"
value={formValues.description}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>File / Image</label>
<input type="file" ref={inputFileRef} />
</div>
<button type="submit" className="color-primary">
Save
</button>
</form>
</>
);
};
Link:
https://codesandbox.io/s/send-form-dcj5v?file=/src/App.js
You can hide your form by change your state on form sumbit and display another component. You have to pass formValue as props in View component. Now think you have better idea what you have to do...
Here i added new component, that display form value on submit
App.js
import { useState, useRef } from "react";
import View from "./View";
const FormX = () => {
const [formValues, setFormValues] = useState({
name: "",
priceUnitary: "",
size: "",
description: ""
});
const [isFormVisible, setIsFormVisible] = useState(true);
const inputFileRef = useRef();
const handleChange = (event) => {
const { name, value } = event.target;
console.log(name, value);
setFormValues({ ...formValues, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formValues);
console.log(inputFileRef?.current?.files);
setIsFormVisible(false);
};
return (
<>
{isFormVisible ? (
<form id="formu" onSubmit={handleSubmit} className="row">
<h1>FORM SEND</h1>
<div className="col-md-6">
<label>Name</label>
<input
placeholder="Text input"
name="name"
value={formValues?.name}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Size</label>
<input
type="number"
placeholder="Text input"
name="size"
value={formValues.size}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Price Unitary</label>
<input
type="number"
placeholder="Text input"
name="priceUnitary"
value={formValues.priceUnitary}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Description</label>
<input
placeholder="Text input"
name="description"
value={formValues.description}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>File / Image</label>
<input type="file" ref={inputFileRef} />
</div>
<button type="submit" className="color-primary">
Save
</button>
</form>
) : (
<View data={formValues} />
)}
</>
);
};
export default FormX;
View.js
import React from "react";
const View = ({ data }) => {
return (
<div>
<p>Name: {data?.name}</p>
<p>priceUnitary: {data?.priceUnitary}</p>
<p>description: {data?.description}</p>
</div>
);
};
export default View;
What could be wrong with these codes? The input is not working once I add [event.target.name]. If I remove that line of codes, I can see the contents that I type inside the input box. The issue is that I want it to work with this code [event.target.name]. This will enable me pick each inputbox values as entered by the user. There are three input boxes and I need to capture the three values in my useState. Any help on how to write it better?
import React, { useState } from 'react';
import "./addbirthday.css";
import "./home.css";
export default function Addbirthday({setShowAdd}) {
const [inputDatas, setInputData] = useState([
{fullName: '', fullDate: '', relationship: ''}
]);
const handlePublish = () =>{
console.log("Hi ", inputDatas)
}
const handleChangeInput = (index, event) =>{
const values = [...inputDatas];
values[index][event.target.name] = event.target.value
setInputData(values)
}
return (
<div className="container">
<div className= { closeInput? "addContainer" :"addWrapper homeWrapper "}>
<i className="fas fa-window-close" onClick={closeNow} ></i>
{inputDatas.map((inputData, index)=> (
<div key={index} className="addbirth">
<label>Name</label>
<input type="text" name="Fname" placeholder='Namend' value=
{inputData.fullName} onChange = {event => handleChangeInput(index, event)} />
<label>Date</label>
<input type="date" placeholder='Date' name="fdate" value=
{inputData.fullDate} onChange = {event => handleChangeInput(index, event)} />
<label>Relationship</label>
<input type="text" placeholder='Friend' name="frelationship" value=
{inputData.relationship} onChange = {event => handleChangeInput(index, event)}/>
</div>
))}
<button className="addBtn" onClick={handlePublish} >Add</button>
</div>
</div>
)
}
You are not setting the name correctly
Change your input tags name to same as state object name meaning
<input name='fullname' />
I have modified your code a bit. Make it as your own and get it done
Upvote my answer if it helps
https://codesandbox.io/s/jolly-khayyam-51ybe?file=/src/App.js:0-1711
import React, { useState } from "react";
export default function Addbirthday({ setShowAdd }) {
const [inputDatas, setInputData] = useState([
{ Fname: "", fdate: "", frelationship: "" }
]);
const handlePublish = () => {
console.log("Hi ", inputDatas);
};
const handleChangeInput = (index, event) => {
const values = [...inputDatas];
values[index][event.target.name] = event.target.value;
setInputData(values);
console.log(values[index][event.target.name]);
};
return (
<div className="container">
<div className={"addContainer addWrapper homeWrapper"}>
<i className="fas fa-window-close"></i>
{inputDatas.map((inputData, index) => (
<div key={index} className="addbirth">
<label>Name</label>
<input
type="text"
name="Fname"
placeholder="Namend"
value={inputData.fullName}
onChange={(event) => handleChangeInput(index, event)}
/>
<label>Date</label>
<input
type="date"
placeholder="Date"
name="fdate"
value={inputData.fullDate}
onChange={(event) => handleChangeInput(index, event)}
/>
<label>Relationship</label>
<input
type="text"
placeholder="Friend"
name="frelationship"
value={inputData.relationship}
onChange={(event) => handleChangeInput(index, event)}
/>
</div>
))}
<button className="addBtn" onClick={handlePublish}>
Add
</button>
</div>
</div>
);
}
I am using react-redux hooks.I want to dispatch an action after some validations when the form is submitted. But I cannot call usedispatch() hook outside function component. Is there any way I can dispatch an action using usedispatch() ?
My code looks as below:
import React, { Component } from 'react';
import { register } from '../../actions/auth';
import { useDispatch, useSelector } from "react-redux";
let state = {
username: '',
password1: '',
password2: '',
}
const onSubmit = (e) => {
e.preventDefault()
const dispatch = useDispatch();
if (state.password1 === state.password2) {
dispatch(register(state))
}
else {
console.log('Psw did not matched.')
}
}
const onChange = (e) => {
let field_name = e.target.name;
state[field_name] = e.target.value;
}
const Register = () => {
return (
<div className="col-md-6 m-auto">
<div className="card card-body mt-5">
<h2 className="text-center">Register</h2>
<form encType="multipart/form-data" onSubmit={onSubmit}>
<div className="form-group">
<label>Username</label>
<input
type="text"
className="form-control"
name="username"
onChange={onChange}
required/>
</div>
<div className="form-group">
<label> Password</label>
<input
type="password"
className="form-control"
name="password1"
onChange={onChange}
required/>
</div>
<div className="form-group">
<label>Confirm Password</label>
<input
type="password"
className="form-control"
name="password2"
onChange={onChange}
required/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">Register</button>
</div>
</form>
</div>
</div>
);
}
export default Register;
I get an error that React Hooks cannot be used outside function component and it is obvious. I am looking for a way to dispatch register action after doing some validation when form is submitted.
Since you are writing a React component, it would make sense if you define your state within and other functions within the component. This way you would be able to use hook, useDispatch as well. Also you can make your input fields controlled instead of letting them be uncontrolled
import React, { Component } from 'react';
import { register } from '../../actions/auth';
import { useDispatch, useSelector } from "react-redux";
const Register = () => {
const [state, setState] = useState({
username: '',
password1: '',
password2: '',
});
const onChange = (e) => {
let field_name = e.target.name;
let field_value = e.target.value;
setState(prev => ({...prev, [field_name]: field_value});
}
const dispatch = useDispatch();
const onSubmit = (e) => {
e.preventDefault()
if (state.password1 === state.password2) {
dispatch(register(state))
}
else {
console.log('Psw did not matched.')
}
}
return (
<div className="col-md-6 m-auto">
<div className="card card-body mt-5">
<h2 className="text-center">Register</h2>
<form encType="multipart/form-data" onSubmit={onSubmit}>
<div className="form-group">
<label>Username</label>
<input
type="text"
className="form-control"
name="username"
value={state.username}
onChange={onChange}
required/>
</div>
<div className="form-group">
<label> Password</label>
<input
type="password"
className="form-control"
name="password1"
value={state.password1}
onChange={onChange}
required/>
</div>
<div className="form-group">
<label>Confirm Password</label>
<input
type="password"
className="form-control"
name="password2"
value={state.password2}
onChange={onChange}
required/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">Register</button>
</div>
</form>
</div>
</div>
);
}
export default Register;