I have a problem with react-hook-form and reactstrap. I have this component List.jsx:
import { useContext, useEffect } from "react";
import { ListContext, ADD_LIST } from '../providers/ShoppingListProvider';
import { Link } from "react-router-dom";
import { useForm } from 'react-hook-form';
import { ListGroup, ListGroupItem, Form, FormGroup, Button, Input, Label, Container, Row, Col } from 'reactstrap';
export const Lists = () => {
const [store, dispatch] = useContext(ListContext);
const { register, handleSubmit, formState: { errors }, getValues } = useForm();
const onSubmit = data => addList(data);
const addList = (data) => {
console.log(data);
//dispatch({ type: ADD_LIST, store: store, listName: data.name });
}
return (
<Container>
<Row>
<Col>
<ListGroup>
{store.lists.map((item, key) => {
return <ListGroupItem key={key} ><Link to={"/list/" + key}>{item.name}</Link></ListGroupItem>
})}
</ListGroup>
</Col>
<Col>
<Form onSubmit={handleSubmit(onSubmit)}>
<FormGroup>
<Label >Name of new list</Label>
<Input type="text" placeholder="name of list" {...register("name", { required: true })} ></Input>
</FormGroup>
<Button type="submit">Přidat list</Button>
</Form>
</Col>
</Row>
</Container>
);
}
the problem is, that when I submit the form, nothing happens (addList won't happen). But when I replace Input (from reactstrap) with normal input from classic html, everything seems working. So the problem is Input from reactstrap. Does anyone knows how to fix this issue?
Thank you very much!
Try like this, look at the innerRef in input
const { register, handleSubmit, formState: { errors } } = useForm();
const { ref, ...rest } = register('name')
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input type="text" placeholder="Name" innerRef={ref} {...rest} />
<Input type="submit" />
</form>
);
Related
I have a form that uses accordion component.
When I print values using watch() then collapse accordion. the values get deleted from inputs when I open it again.
This behaviour is not happening when I don't use watch()
I would like to know why this is happening ? watch() should only listen to data as I know.
CodeSandbox
CreateTest.ts
import { QuestionCreatingForm } from "./QuestionForm";
import {
AccordionHeader,
AccordionItem,
AccordionPanel,
Accordion
} from "#fluentui/react-components";
import { Button, Form } from "#fluentui/react-northstar";
import { useFieldArray, useForm } from "react-hook-form";
export function CreateTest() {
const methods = useForm();
const { control, register, handleSubmit, watch } = methods;
const { fields, append } = useFieldArray({
control,
name: "questions"
});
const addQuestion = (event: any) => {
event.preventDefault();
append({ name: "" });
};
const onSubmit = (data: any) => {
alert(JSON.stringify(data));
};
return (
<div className="w-8/12 m-auto">
{JSON.stringify(watch())}
<Form onSubmit={handleSubmit(onSubmit)}>
{fields.map((field, index) => (
<Accordion key={field.id} collapsible>
<AccordionItem value="1">
<AccordionHeader>Accordion Header </AccordionHeader>
<AccordionPanel>
<QuestionCreatingForm
fieldId={field.id}
index={index}
{...{ control, register, watch }}
/>
</AccordionPanel>
</AccordionItem>
</Accordion>
))}
<Button
className="my-10"
content="Add question"
primary
fluid
onClick={addQuestion}
/>
<Button
className="my-10"
fluid
content="submit"
primary
type="submit"
/>
</Form>
{/* </FormProvider> */}
</div>
);
}
QuestionForm.ts
import {
Button,
Divider,
FormCheckbox,
FormInput,
TrashCanIcon
} from "#fluentui/react-northstar";
import { SyntheticEvent } from "react";
import {
Control,
FieldValues,
useFieldArray,
UseFormRegister,
UseFormWatch
} from "react-hook-form";
export function QuestionCreatingForm({
index,
fieldId,
control,
register,
watch
}: {
index: number;
fieldId: string;
control: Control<FieldValues, any>;
register: UseFormRegister<FieldValues>;
watch: UseFormWatch<FieldValues>;
}) {
const { fields, append, remove } = useFieldArray({
control,
name: `questions.${index}.responses`
});
const addResponse = (event: SyntheticEvent<HTMLElement, Event>) => {
event.preventDefault();
append({ name: "" });
};
const deleteResponse = (index: number) => {
remove(index);
};
return (
<>
<FormInput
label="Question"
required
fluid
key={index}
{...register(`questions.${index}.name` as const)}
/>
<div className="w-10/12 m-auto">
{fields.map((field, i) => (
<div className="flex w-full">
<FormCheckbox />
<div className="w-full" key={field.id}>
<FormInput
{...register(`questions.${index}.responses.${i}.name` as const)}
defaultValue=""
label={`reponses ${i + 1}`}
required
fluid
/>
</div>
<Button
text
styles={{ color: "red", placeSelf: "end" }}
icon={<TrashCanIcon />}
onClick={(e) => deleteResponse(i)}
iconOnly
/>
</div>
))}
<Button
content="Ajouter une réponse"
tinted
fluid
onClick={addResponse}
/>
</div>
<Divider />
</>
);
}
So basically I have created a form using react bootstrap. I have used onchange property to set the paymentMethod state according to the option selected. But the problem is:
The radio checkbox is only working once. When I click on the stripe checkbox, the state changes. But now when I click paypal again, state doesn't change.
I can't figure out where I went wrong or why is this happening.
import React, { useState } from "react";
import { Form, Row, Col, Button } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { savePaymentMethod } from "../actions/cartActions";
import CheckoutSteps from "../components/checkout/CheckoutSteps";
import FormContainer from "../components/FormContainer";
const PaymentPage = () => {
const shippingAddress = useSelector((state) => state.cart.shippingAddress);
const [paymentMethod, setPaymentMethod] = useState("Stripe");
console.log(paymentMethod);
const dispatch = useDispatch();
const navigate = useNavigate();
if (!shippingAddress) {
navigate("/shipping");
}
function submitHandler(e) {
e.preventDefault();
dispatch(savePaymentMethod(paymentMethod));
navigate("/placeorder");
}
return (
<>
<CheckoutSteps step1 step2 step3 />
<FormContainer>
<h2>Payment Method</h2>
<Form onSubmit={submitHandler}>
<Form.Group>
<Form.Label className='my-3' as='legend'>
Select Method
</Form.Label>
<Col>
<Form.Check
className='my-3'
type='radio'
label='PayPal or Credit Card'
id='PayPal'
name='paymentMethod'
value='PayPal'
checked
onChange={(e) => setPaymentMethod(e.target.value)}
/>
<Form.Check
className='my-3'
type='radio'
label='Stripe'
id='Stripe'
name='paymentMethod'
value='Stripe'
onChange={(e) => setPaymentMethod(e.target.value)}
/>
</Col>
</Form.Group>
<Button type='submit' variant='primary' className='my-3'>
Continue
</Button>
</Form>
</FormContainer>
</>
);
};
export default PaymentPage;
The checked prop should be dynamic based on the selected radio. And you can use a single function for onChange:
const PaymentPage = () => {
const [paymentMethod, setPaymentMethod] = useState("Stripe");
const onPaymentMethodChange = ({ target: { value } }) => {
setPaymentMethod(value);
};
return (
<>
<Form.Group>
<Form.Label className="my-3" as="legend">
Select Method
</Form.Label>
<Col>
<Form.Check
className="my-3"
type="radio"
label="PayPal or Credit Card"
id="PayPal"
name="paymentMethod"
value="PayPal"
checked={paymentMethod === "PayPal"}
onChange={onPaymentMethodChange}
/>
<Form.Check
className="my-3"
type="radio"
label="Stripe"
id="Stripe"
name="paymentMethod"
value="Stripe"
checked={paymentMethod === "Stripe"}
onChange={onPaymentMethodChange}
/>
</Col>
</Form.Group>
</>
);
};
i'm quite new with react and i'm building a form with react-hook and useState to manage my datas after the submit.
I'm not able to use textfield as they are blocked. I think that i make some errors into value/onChange parameters but i don't know what type of error.
import React, { useState } from "react";
import {
TextField,
MenuItem,
Typography,
Checkbox,
Divider,
Button,
} from "#mui/material";
import { MdError } from "react-icons/md";
import { BsArrowRight } from "react-icons/bs";
import "../style/contactform.scss";
import { useForm } from "react-hook-form";
const initialState = {
name: "",
email: "",
};
const ContactForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const [state, setState] = useState(initialState);
const { name, email } = state;
const handleInputChange = (e) => {
const { name, value } = e.target;
setState({ ...state, [name]: value });
};
const onSubmit = (e) => {
e.preventDefault();
console.log("form submit");
setState(initialState);
};
return (
<form className="contact-form" onSubmit={handleSubmit(onSubmit)}>
<Typography variant="h4" className="form-title">
Be the first.
</Typography>
<div className="form-component">
<TextField
id="standard-basic"
label="Nome*"
variant="standard"
name="nome"
value={name}
onChange={handleInputChange}
{...register("nome", {
required: true,
})}
/>
{errors?.nome?.type === "required" && (
<MdError className="form-validation-icon" />
)}
</div>
<Divider className="form-hr" />
<div className="form-component">
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
value={email}
onChange={handleInputChange}
{...register("email", {
required: true,
pattern: {
value:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
},
})}
/>
{errors?.email?.type === "required" && (
<MdError className="form-validation-icon" />
)}
{errors?.email?.type === "pattern" && (
<Typography variant="p" className="form-validation-email">
Inserisci un indirizzo email valido.
</Typography>
)}
</div>
<Divider className="form-hr" />
<Button className="form-submit" type="submit" variant="contained">
<BsArrowRight />
</Button>
</form>
);
};
export default ContactForm;
Textfields are completely block but initial state is actually working, do i miss something?
Can you help me?
To assign initial values using the useForm hook, you pass it under the defaultValues parameter passed to the hook like so:
const {
register,
handleSubmit,
reset
formState: { errors },
} = useForm({
defaultValues: initialState
});
Then just pass the ...register name and email to the inputs. There is no need to assign values to them again:
<TextField
id="standard-basic"
label="Name*"
variant="standard"
name="name"
{...register("name", {
required: true,
})}
/>
// for the email..
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
{...register("email", {
required: true,
pattern: {
value: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,},
})}
/>
If you'll notice, the values are off the text fields already and there's also no need for the handleInputChange function. The useForm hook takes care of that.
Edit:
In addition to the onSubmit function, the handleSubmit from useForm passes a data object into the function like this:
const onSubmit = (data) => {
console.log("form submitted", data);
reset(); // this can be destructured of the `useForm` hook.
};
For more info check their documentation
I am trying to get the state from redux store and trying to fill the input field from state. If user in edit mode. In edit mode, we normally show the prefilled value in input field. But what is wrong with the below approach?
I am able to store single user successfully in reducer but in component i am not getting. Sometimes i get the value. Overall, it's very inconsistent.
import React, { useState, useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import { useSelector, useDispatch } from "react-redux";
import { useHistory, useParams } from "react-router-dom";
import { addUser, getSingleUser, updateUser } from "../redux/actions";
const useStyles = makeStyles((theme) => ({
root: {
marginTop: 100,
"& > *": {
margin: theme.spacing(1),
width: "45ch",
},
},
}));
const initialState = {
name: "",
address: "",
contact: "",
email: "",
};
const EditUser = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.users);
console.log("user", user);
const [state, setState] = useState(user);
const [error, setError] = useState("");
const { name, address, email, contact } = state;
const classes = useStyles();
const history = useHistory();
let dispatch = useDispatch();
const onInputChange = (e) => {
let { name, value } = e.target;
setState({ ...state, [name]: value });
};
useEffect(() => {
dispatch(getSingleUser(id));
}, []);
const handlSubmit = (e) => {
e.preventDefault();
console.log("name", name);
if (!name || !email || !address || !contact) {
setError("Please fill all Input Field");
} else {
dispatch(updateUser(state, id));
setError("");
history.push("/");
}
};
return (
<>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
type="submit"
color="secondary"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit user</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form
className={classes.root}
noValidate
autoComplete="off"
onSubmit={handlSubmit}
>
<TextField
id="standard-basic"
label="Name"
value={name}
name="name"
onChange={onInputChange}
type="text"
/>
<br />
<TextField
id="standard-basic"
value={email}
name="email"
label="Email"
type="email"
onChange={onInputChange}
/>
<br />
<TextField
id="standard-basic"
value={contact}
name="contact"
label="Contact"
type="number"
onChange={onInputChange}
/>
<br />
<TextField
id="standard-basic"
label="Address"
value={address}
name="address"
type="text "
onChange={onInputChange}
/>
<br />
<Button
style={{ width: "100px" }}
variant="contained"
type="submit"
color="primary"
>
Update
</Button>
</form>
</>
);
};
export default EditUser;
Below is redux actions logic to get the single user and dispatching an action to store single user value in reducer.
export const getSingleUser = (id) => {
return function (dispatch) {
axios
.get(`${process.env.REACT_APP_API}/${id}`)
.then((resp) => {
console.log("resp", resp);
dispatch(singleUser(resp.data));
})
.catch((error) => console.log(error));
};
};
following is the AddCourse page
AddCourse.js
import React, { useEffect, useState } from 'react';
import { Button, Form, FormGroup, Label, Input, FormText, Container } from 'reactstrap';
import database from '../services/fire';
import { useSelector, useDispatch } from 'react-redux';
import uuid from 'react-uuid';
import '../App.css';
const AddCourse = () => {
const [courseId, setCourseId] = useState('');
const [courseTitle, setCourseTitle] = useState('');
const [courseDesc, setCourseDesc] = useState('');
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user.uid);
useEffect(() => {
document.title = "Add Courses"
}, [])
const addCourse = () => {
const payload = { id: uuid(), courseId:courseId, courseTitle: courseTitle, courseDesc: courseDesc }
const dbcoursesWrapper = database.ref().child(user).child('courses');
// const dbcoursesWrapper = database.ref(`users/${user}/courses`).push(courseId, courseTitle, setCourseDesc);
return dbcoursesWrapper.child(payload.id).update(payload).then(() => {
setCourseId('');
setCourseTitle('');
setCourseDesc('');
dispatch({ type: "ADD_COURSES", payload });
})
}
return (
<div>
<h1 className="text-center my-3">Fill Course Detail</h1>
<Form onSubmit={(e) => {
e.preventDefault(e.target.value);
addCourse();
}}>
<FormGroup>
<label for="UserId">Course Id</label>
<Input
type="text"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
placeholder="Enter your Id"
name="userId"
id="UserId"
/>
</FormGroup>
<FormGroup>
<label for="title">Course Title</label>
<Input
type="text"
value={courseTitle}
onChange={(e)=> setCourseTitle(e.target.value)}
placeholder="Enter Course Title"
name="title"
id="title"
/>
</FormGroup>
<label for="description">Course Description</label>
<Input
value={courseDesc}
onChange={(e) => setCourseDesc(e.target.value)}
type="textarea"
placeholder="Enter Course Description"
name="description"
id="description"
style={{ height: 150 }}
/>
<Container className="text-center">
<Button color="success" type='submit'>Add Course</Button>
<Button color="warning ml-3">clear</Button>
</Container>
</Form>
</div>
);
};
export default AddCourse;
courses.js here is the update button when i click on it i want it to open the AddCourse page with the same values of the course i want to update not getting any clue how can i do this
import React from 'react';
import {
Card, CardText, CardBody,
CardTitle, Button, Container
} from 'reactstrap';
import database from '../services/fire';
import { useSelector, useDispatch } from 'react-redux';
import { fetchCourse } from '../actions/courses';
import AddCourse from './AddCourse';
const Course = ({course}) => {
const user = useSelector(state => state.auth.user.uid);
const dispatch = useDispatch();
const removeCourse = (id) => {
console.log(id);
const dbtasksWrapper = database.ref().child(user).child('courses');
dbtasksWrapper.child(id).remove().then(() => {
dispatch({ type: 'DELETE_COURSE', id: id })
dispatch(fetchCourse(user));
})
}
return (
<div>
<Card>
<CardBody className="text-center ">
<CardText className="text-center"><h2>CourseID: {course.courseId}</h2></CardText>
<CardTitle className="font-weight-bold text-center"><h1>{course.courseTitle}</h1></CardTitle>
<CardText className="text-center">{course.courseDesc}.</CardText>
<Container className="text-center">
{/* here is the update button and when onclick its goes to add course page with the course vale need to update** */}
<Button color="warning"onClick={}>Update</Button>
<Button color="danger ml-4" onClick={()=>removeCourse(course.id)}>Delete</Button>
</Container>
</CardBody>
</Card>
</div>
);
};
export default Course;
Sorry, not getting your question properly. You are trying to add a course using AddCourse.js component on submitting the form, then you want to display the course ID, Title and Description. In order to do this, you need the following:
1 - localStorage,
2 - Context API or Redux,
3 - Create a new state on your Context API or redux to store the values and pass it down to children components, in your example courses.js
If I understand correctly you want to switch between viewing a course and editing/updating a course?
One way to achieve this is:
const Course = ({ course }) => {
const user = useSelector((state) => state.auth.user.uid);
const dispatch = useDispatch();
const removeCourse = (id) => {
console.log(id);
const dbtasksWrapper = database.ref().child(user).child('courses');
dbtasksWrapper
.child(id)
.remove()
.then(() => {
dispatch({ type: 'DELETE_COURSE', id });
dispatch(fetchCourse(user));
});
};
// state to switch between updating the course and viewing the course
const [isUpdating, setIsUpdating] = useState(false);
return (
<div>
{isUpdating ? (
{/* pass the course down and a callback to close update component */}
<AddCourse course={course} finishUpdate={() => setIsUpdating(false)} />
) : (
<Card>
<CardBody className="text-center ">
<CardText className="text-center">
<h2>CourseID: {course.courseId}</h2>
</CardText>
<CardTitle className="font-weight-bold text-center">
<h1>{course.courseTitle}</h1>
</CardTitle>
<CardText className="text-center">{course.courseDesc}.</CardText>
<Container className="text-center">
{/* Set isUpdating to true */}
<Button color="warning" onClick={() => setIsUpdating(true)}>
Update
</Button>
<Button color="danger ml-4" onClick={() => removeCourse(course.id)}>
Delete
</Button>
</Container>
</CardBody>
</Card>
)}
</div>
);
};
This will change depending on your setup, if this isn't what you wanted please provide some more details about how you would like this to function.