I am trying to generate a new form when you click a button. I want to do this with hooks. I am struggling to wrap my head around what I need to do, below is an example function and the button. Any help is appreciated.
const addStudent = () => {
console.log("clicked")
return(
<Container>
<Form form="First Name" type="text" placeholder="First Name"/>
<Form form="Last Name" type="text" placeholder="Last Name"/>
<Form form="Date of Birth" type="date"/>
<Form form="School ID" type="text"/>
<Form form="Campus" type="text"/>
<Form form="School Grade" type="text"/>
</Container>
)
console.log(setStudent)
}
<button onClick={addStudent}>Add Another Student</button>
You can do something like this
https://codesandbox.io/s/silly-nash-0slhh?file=/src/App.js:97-1145
App.js
import Input from "./Input";
export default function App() {
const [students, setStudents] = useState([]);
const addStudent = () => {
setStudents(students => [...students, <Input />]);
console.log("dddd", students);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={addStudent}>Add Another Student</button>
{students.map((item, i) => (
<div key={i}>{item}</div>
))}
</div>
);
}
Input.js
import React, { useState } from "react";
export default function Input() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastname] = useState("");
const [Dob, setDob] = useState("");
return (
<div style={{ width: "200px", height: "100px", padding: "5px" }}>
<input
type="text"
value={firstName}
onChange={e => setFirstName(e.target.value)}
placeholder="First Name"
/>
<input
type="text"
value={lastName}
onChange={e => setLastname(e.target.value)}
placeholder="Last Name"
/>
<input
type="text"
value={Dob}
onChange={e => setDob(e.target.value)}
placeholder="DOB"
/>
</div>
);
}
I created a variable called students and using Context for capturing the state values. I then mapped through students and then for the addStudent function just used the spread operator and set setStudents below.
const [students, setStudents] = useContext(FormDataContext);
*this is in the return()*
{students.map((student, index) => (
<FormCard
key={index}
index={index}
handleChange={handleChange}
student={students[index]}
metadata={metadata}
/>
))}
*then called this function in my button onClick*
const addStudent = () => {
setStudents([...students, {}]);
};
Related
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>
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;
So I've created inputs in two functions and on Onchange it gets the value that is being input into them. But anytime I type into the inputs I can only type a letter at a time before it is unfocused and I believe this is because the component keeps on rendering.
I've also realized that whenever I remove the Onchange and value props then everything goes back to normal and I'm able to type everything easily. How would I fix this?
App.js
import style from "./auth.module.css";
import { useEffect, useRef, useState } from "react";
import { useAuthState } from 'react-firebase-hooks/auth';
import { auth, signInWithEmailAndPassword, signInWithGoogle, registerWithEmailAndPassword } from "../../firebase";
import { CSSTransition } from "react-transition-group";
function Auth() {
const [activeMenu, setActiveMenu] = useState("main");
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, loading, error] = useAuthState(auth);
const Emailform = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Email:</label>
<form className={style.input}>
<input
type="email"
name="email"
required="true"
value={email}
onChange={(e) => setEmail(e.target.value)} />
</form>
</div>
);
}
const Passform = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Password:</label>
<form className={style.input}>
<input
type="password"
name="password"
required="true"
value={password}
onChange={(e) => setPassword(e.target.value)} />
</form>
</div>
);
}
let domNode = useClickOutside(() => {
setActiveMenu(false);
});
return (
<div className={style.container}>
<Login />
<Signup />
</div>
);
function AuthType(props) {
return (
<a
href={props.link}
className={style.menuItem}
onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}
>
{props.children}
</a>
);
}
/* Login */
function Login() {
return (
<CSSTransition in={activeMenu === "main"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.login}>
<h1 className={style.title}>Clip It</h1>
{/* Email and Password */}
<Emailform/>
<Passform/>
<div className={style.button}>
<input
type="submit"
value="Login"
onClick={() => signInWithEmailAndPassword(email, password)} />
<input
type="submit"
value="Login with Google"
onClick={signInWithGoogle} />
</div>
<div className={style.text}>
<p className={style.plink}>Forgot Password</p>
<div>
Need an account?
<AuthType goToMenu="Signup">click here</AuthType>
</div>
</div>
</div>
</div>
</CSSTransition>
);
}
function Signup() {
return (
<CSSTransition in={activeMenu === "Signup"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.signUp}>
<div className={style.title}> Clip It</div>
<Form label="First Name" type="text" />
<Form label="Last Name" type="Text" />
<Form label="Email" type="email"/>
<Form label="Date of Birth" type="date" />
<Form label="Password" type="password"/>
<Form label="Confirm Password" type="password" />
<div className={style.button}>
<input type="submit" value="Sign Up" />
</div>
<div className={style.text}>
have an
<AuthType goToMenu="main"> account</AuthType>
</div>
</div>
</div>
</CSSTransition>
);
}
}
let useClickOutside = (handler) => {
let domNode = useRef();
useEffect(() => {
let clickListener = (event) => {
if (domNode.current && !domNode.current.contains(event.target)) {
handler();
}
};
document.addEventListener("mousedown", clickListener);
return () => {
document.removeEventListener("mousedown", clickListener);
};
});
return domNode;
};
function Form(props) {
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type={props.input}
name={props.input}
required="true" />
</form>
</div>
);
}
export default Auth;
Don't put your component definition inside the other component. If you do it, you create a new component at every render.
You need to work around to move Emailform, Passform to outside of Auth.
Checkout this sandbox to experiment
function Auth() {
// 🚫 the bad place to define components
const Emailform = () => {
...
}
// 🚫 the bad place to define components
const Passform = () => {
...
}
}
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 having an issue with props updating with Redux. I am using redux-promise-middleware, and am making a login request. My hook receives the initialState. I can see the dispatch and reducer are being correctly updated when I console.log in the mapStateToProps function. However, the props are never updated with redux.
const Login = (props) => {
const [email, updateEmail] = useState('')
const [password, updatePassword] = useState('')
const [firstName, updateFirstName] = useState('')
const [lastName, updateLastName] = useState('')
const [loginToggle, updateLoginToggle] = useState(true)
async function handleLogin() {
const loginInfo = {email, password}
await props.loginUser(loginInfo)
if (props.user.loggedIn) props.history.push('/dashboard');
}
return (
<div className="login-page">
<div className="login-register-container">
{ loginToggle ?(
<div className="login-container">
<h2>Login</h2>
<input placeholder='Email' value={email} onChange={e =>
updateEmail(e.target.value)} />
<input placeholder='Password' value={password} onChange={e => updatePassword(e.target.value)} />
<button onClick={handleLogin}> Login </button>
<p style={{cursor:"pointer"}} onClick={() => updateLoginToggle(false)}> need to register?</p>
</div>
):(
<div className="register-container">
<h2>Register</h2>
<input placeholder='First Name' value={firstName} onChange={e => updateFirstName(e.target.value)} />
<input placeholder='Last Name' value={lastName} onChange={e => updateLastName(e.target.value)} />
<input placeholder='Email' value={email} onChange={e => updateEmail(e.target.value)} />
<input placeholder='Password' value={password} onChange={e => updatePassword(e.target.value)} />
<button onClick={handleRegister}> Register </button>
<p style={{cursor:"pointer"}} onClick={() => updateLoginToggle(true)}> already have login?</p>
</div>
)}
</div>
</div>
)
}
function mapStateToProps(reduxState) {
console.log(reduxState)
return {
user: reduxState.user
}
};
export default connect(mapStateToProps, {loginUser, getUser})(Login)