I am using react hooks and I have a child component Addmember and a parent component MemberList. Within the member list the user clicks add member and a model pops up (the child component) and then user can input some form data then submit the form. The form then adds the new member to the MongoDB collection. .then() it called parentCallback(true) to flip the parent state of loading to true which will then trigger a component re-render. Which is successfully working.
The problem here which I cant get my head around the issue is that upon the parent re-render when it hits the database to getMembers its not returning the newly added member. If I refresh the page the same call will successfully grab all the members including the recent addition.
MemberList.js:
import React, { useState, useEffect } from "react";
import Spinner from "react-bootstrap/Spinner";
import { toast } from "react-toastify";
import { css } from "glamor";
import "react-toastify/dist/ReactToastify.css";
import "react-datepicker/dist/react-datepicker.css";
import "../Styles/ReactDatePicker.css";
import Member from "./Member";
import AddMemberModal from "../models/AddMember";
import RemoveMemberModel from "../models/RemoveMember";
const { getFamily, getMembers } = require("../Utils/Service");
const MemberList = () => {
const [family, setFamily] = useState({});
const [familyMembersList, setFamilyMembersList] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (loading) {
fetchData();
}
}, [loading]);
async function fetchData() {
getFamily.then(result => {
console.log('get family');
setFamily(result);
});
getMembers.then(result => {
try {
console.log('set family list');
console.log(result);
setFamilyMembersList(
result.map((child, index) => (
<Member
key={index}
index={child._id}
balance={child.balance}
firstName={child.firstName}
lastName={child.lastName}
birthday={child.birthday}
role={child.role[0]}
/>
))
);
} catch (e) {
toast.error("500: Error with Service Call", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
className: css({
background: "#ed5565 !important"
})
});
} finally {
setLoading(false);
}
});
}
function handleCallback() {
setLoading(true);
}
return (
<div className="ibox">
<div className="ibox-title">
<h5>{family.name}</h5>
<div className="ibox-tools">
<span className="label label-warning-light float-right">
{familyMembersList.length} Member(s)
</span>
</div>
</div>
<div className="ibox-content">
<div className="feed-activity-list">
{loading ? (
<Spinner animation="grow" role="status" variant="dark">
<span className="sr-only">Loading...</span>
</Spinner>
) : (
familyMembersList
)}
</div>
<div className="d-flex">
<a
className="btn btn-primary text-white m-t"
data-toggle="modal"
data-target={"#newMemberModel"}
>
<i className="fa fa-plus"></i> New Member
</a>
<a
className="btn btn-danger text-white m-t m-l"
data-toggle="modal"
data-target={"#removeMemberModel"}
>
<i className="fa fa-minus"></i> Remove Member
</a>
</div>
</div>
{/* Add Member Model */}
<AddMemberModal parentCallback={handleCallback}/>
{/* Remove Member Model */}
<RemoveMemberModel />
</div>
);
};
export default MemberList;
AddMember.js
import React, { useState, useEffect } from "react";
import { toast } from "react-toastify";
import DatePicker from "react-datepicker";
import useForm from "react-hook-form";
import { css } from "glamor";
import "react-toastify/dist/ReactToastify.css";
import "react-datepicker/dist/react-datepicker.css";
import "../Styles/ReactDatePicker.css";
const { postMember } = require("../Utils/Service");
const AddMemberModal = ({parentCallback}) => {
const [date, setDate] = useState(new Date());
const { handleSubmit, register, errors } = useForm();
// const handleDateChange = date => {
// setDate(date)
// }
const onSubmit = (data) => {
try {
data.familyId = "5dddf14df965552b3da57be1";
postMember(data).then(async () => parentCallback(true));
} catch (error) {
toast.error("500: Error with Service Call", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
className: css({
background: "#ed5565 !important"
})
});
}
};
return (
<div
className="modal inmodal"
id={"newMemberModel"}
tabIndex="-1"
role="dialog"
style={{ display: "none" }}
aria-hidden="true"
>
<div className="modal-dialog">
<div className="modal-content animated fadeIn">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span className="sr-only">Close</span>
</button>
<h4 className="modal-title">New Family Member</h4>
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="modal-body">
<div className="row">
<div className="col">
<div className="form-group">
<label>First Name</label>
<input
type="text"
placeholder="First Name"
className="form-control"
name="firstName"
ref={register({
required: true,
pattern: {
value: /^[a-zA-Z]+$/i,
message: "Invalid First Name"
}
})}
/>
<div className="text-danger">
{errors.firstName && errors.firstName.message}
</div>
</div>
<div className="form-group">
<label className="font-normal">Birthday</label>
<div className="input-group date">
{/* <DatePicker
selected={date}
onChange={handleDateChange}
placeholderText="Click to select a date"
isClearable
peekNextMonth
showMonthDropdown
showYearDropdown
dropdownMode="select"
ref={e =>register({
name: "Birthday",
required: false
})}
/> */}
<input
type="text"
placeholder="01/01/01"
className="form-control"
name="birthDate"
ref={register({
required: false
})}
/>
</div>
</div>
</div>
<div className="col">
<div className="form-group">
<label>Last Name</label>
<input
type="text"
placeholder="Last Name"
className="form-control"
name="lastName"
ref={register({
required: true,
pattern: {
value: /^[a-zA-Z]+$/i,
message: "Invalid Last Name"
}
})}
/>
<div className="text-danger">
{errors.lastName && errors.lastName.message}
</div>
</div>
<div className="form-group">
<label>Role</label>
<select
className="custom-select"
name="role"
ref={register({ required: true })}
>
<option defaultValue>Select Role</option>
<option value="Adult">Adult</option>
<option value="Child">Child</option>
</select>
</div>
</div>
</div>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-white"
data-dismiss="modal"
>
Close
</button>
<button type="submit" className="btn btn-primary">
Add Member
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default AddMemberModal;
The order in which I can see in the console of the console happening is:
[Log] get family
[Log] set family list
[Log] [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object] (10)
The object its return is missing the most recent member addition. I have tried adding timeouts thinking it may be a timing issue but this had no change in this issue.
you can add the submitted data to the array instead of refreshing the page
in MemberList.js file, change your handleCallback to this :
function handleCallback(data){
const arr = [...familyMembersList];
let newMember = (
<Member
key={data._id}
index={data._id}
balance={0}
firstName={data.firstName}
lastName={data.lastName}
birthday={data.birthday}
role={data.role}
/>
)
arr.push(newMember);
console.log(arr);
setFamilyMembersList(arr);
setLoading(false);
}
and in your AddMember.js change onSubmit method to this :
const onSubmit = (data) => {
try {
data.familyId = "5dddf14df965552b3da57be1";
postMember(data).then(async () => parentCallback(data));
} catch (error) {
...
}
};
Related
Hi guys i want to pass 'error' coming from nodejs backend to onsubmit the problem that even if the error passes i still navigate to login page
this is my code :
import "./form_inscription.css";
import { useState } from "react";
import { useFormik } from "formik";
import { validationSchemaBase } from "./formsScema";
//reactstrap componenets
import { FormGroup, Label, Input, Alert } from "reactstrap";
//icons and assets
import ArrowLeft from "../../../../assets/icons/Arrow-Left.png";
import { useDispatch, useSelector } from "react-redux";
// import { registerUser } from "../../../../store/features/authentication/inscription/inscriptionThunk";
import { registerUser } from "../../../../store/features/authentication/authentificationThunk";
import { useNavigate } from "react-router-dom";
import { Eye, EyeOff } from "react-feather";
import { useEffect } from "react";
const FormIscriptionEtape1 = () => {
const [checkboxError, setCheckboxError] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const { error } = useSelector((store) => store.authentification);
const onSubmit = (values) => {
if (values.password === values.passConfirm) {
//checkbox error handling conditions
if (values.cPartner || values.aPartner || values.sPartner) {
setCheckboxError(false);
dispatch(registerUser(values));
if (!error) {
navigate("/login");
}
} else {
setCheckboxError(true);
}
//catch error handling
} else {
console.log("mot de passe non conforme!!");
}
};
const togglePasswordVisibility = () => {
setShowPassword((prevState) => !prevState);
};
const dispatch = useDispatch();
//formik data management
const {
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
errors,
touched,
} = useFormik({
initialValues: {
email: "",
password: "",
passConfirm: "",
cPartner: false,
aPartner: false,
sPartner: false,
},
validationSchema: validationSchemaBase.test(
"one check-box-required",
"Veuillez sélectionner le type de votre organsime",
(values) => values.aPartner || values.sPartner || values.cPartner
),
onSubmit,
});
//handle check boxes
const handleChangeBox = (event) => {
const { name, value, checked } = event.target;
if (name === "cPartner" || name === "aPartner" || name === "sPartner") {
setFieldValue(name, checked);
if (name === "cPartner") {
setFieldValue("aPartner", false);
setFieldValue("sPartner", false);
setCheckboxError(false);
} else if (name === "aPartner") {
setFieldValue("cPartner", false);
setFieldValue("sPartner", false);
setCheckboxError(false);
} else {
setFieldValue("cPartner", false);
setFieldValue("aPartner", false);
setCheckboxError(false);
}
} else {
setFieldValue(name, value);
setCheckboxError(false);
}
};
//navigation to Login screen through the back button temporary will be deleted in the future
const navigate = useNavigate();
const handleNavigateLogin = () => {
navigate("/login");
};
return (
<div>
<div className="w-100 d-flex flex-column justify-content-center align-items-center">
<div className="w-50 mb-1">
<FormGroup className="mt-2">
<Label
className="label_title_inscription text_600_nrm"
for="exampleEmail"
>
Email
</Label>
<Input
className="input_text_off"
type="email"
name="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && (
<p className="validation_problems">{errors.email}</p>
)}
</FormGroup>
</div>
<div className="w-50 mb-1">
<div className=" justify-content-between">
<FormGroup className="auth-login-form mt-2">
<Label
className="label_title_inscription text_600_nrm"
for="examplePassword"
>
Mot de passe
</Label>
<Input
className="input_text_off"
type={showPassword ? "text" : "password"}
name="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
/>
{/* <div
className="password-visibility-icon"
onClick={togglePasswordVisibility}
>
{showPassword ? <Eye size={20} /> : <EyeOff size={20} />}
</div> */}
{errors.password && touched.password && (
<p className="validation_problems">{errors.password}</p>
)}
</FormGroup>
</div>
</div>
<div className="w-50 mb-1">
<div className=" justify-content-between">
<FormGroup className="auth-login-form mt-2">
<Label
className="label_title_inscription text_600_nrm"
for="examplePassword"
>
Confirmation mot de passe
</Label>
<Input
className="input_text_off"
type={showPassword ? "text" : "password"}
name="passConfirm"
value={values.passConfirm}
onChange={handleChange}
onBlur={handleBlur}
/>
{/* <div
className="password-visibility-icon"
onClick={togglePasswordVisibility}
>
{showPassword ? <Eye size={20} /> : <EyeOff size={20} />}
</div> */}
{errors.passConfirm && touched.passConfirm && (
<p className="validation_problems">{errors.passConfirm}</p>
)}
</FormGroup>
</div>
</div>
</div>
{/* checkbox section */}
<p className="text-center mt-5 checkbox_text_inscription text_600_nrm">
Vous êtes
</p>
{checkboxError && (
<p className="validation_problems error_validation_check">
Veuillez sélectionner le type de votre organsime
</p>
)}
<div className="d-flex justify-content-center checkbox">
<div className="form-check d-flex checks_gap">
<Input
type="checkbox"
className="form-check"
name="cPartner"
checked={values.cPartner}
onChange={handleChangeBox}
/>
<Label
className="form-check-Label checkbox_text_inscription text_600_nrm"
htmlFor="formrow-customCheck"
>
Commercial
</Label>
</div>
<div className="form-check d-flex checks_gap">
<Input
type="checkbox"
className="form-check"
name="sPartner"
checked={values.sPartner}
onChange={handleChangeBox}
/>
<Label
className="form-check-Label checkbox_text_inscription text_600_nrm"
htmlFor="formrow-customCheck"
>
Educatif
</Label>
</div>
<div className="form-check d-flex checks_gap">
<Input
type="checkbox"
className="form-check"
name="aPartner"
checked={values.aPartner}
onChange={handleChangeBox}
/>
<Label
className="form-check-Label checkbox_text_inscription text_600_nrm"
htmlFor="formrow-customCheck"
>
Associatif
</Label>
</div>
</div>
{error ? <Alert color="danger">{error.message}</Alert> : null}{" "}
<div className="w-100 d-flex justify-content-end mt-5">
{/* <button
className="d-flex align-items-center justify-content-center btn_next_back"
onClick={handleNavigateLogin}
>
<img src={ArrowLeft} alt="" />
</button> */}
<button type="submit" className="btn_next_step" onClick={handleSubmit}>
Suivant
</button>
</div>
</div>
);
};
export default FormIscriptionEtape1;
i tried passing error to onsubmit props but it gives me undefined everytime so the only way that my error passes is the way i am writing the code right now.
Maybe it's because of this typo?
(store) => store.authentification
it should probably be "authentication"
(store) => store.authentication
1st component (here is the modal)
import './Register.css';
import {useFormik} from 'formik';
import * as Yup from 'yup';
import {useNavigate} from 'react-router-dom';
import {registerUser} from '../../services/auth.service';
import {
createUser,
getUserByUsername,
} from '../../services/user.request.services.js';
import {useContext} from 'react';
import AppContext from '../../context/AppContext.js';
export default function Register() {
const navigate = useNavigate();
const {addToast} = useContext(AppContext);
const formik = useFormik({
initialValues: {
username: '',
firstName: '',
lastName: '',
email: '',
password: '',
},
validationSchema: Yup.object({
username: Yup.string()
.max(50, 'Username must be maximum 50 symbols')
.required('Please, choose username.')
.min(4, 'username must be minimum 4 symbols'),
firstName: Yup.string()
.max(32, 'Last name must be maximum 32 symbols')
.min(4, 'First name must be minimum 4 symbols')
.required('You need to apply first name!'),
lastName: Yup.string()
.max(32, 'First name must be maximum 32 symbols')
.min(4, 'Last name must be minimum 4 symbols')
.required('You need to apply last name!'),
email: Yup.string()
.email('Must be a valid email address')
.required('Email is required!'),
password: Yup.string().required('Please, type your password.'),
}),
onSubmit: async (values) => {
try {
const user = await getUserByUsername(values.username);
if (user !== null) {
formik.errors.username = 'Username already in use!';
return;
}
const credentials = await registerUser(values.email, values.password);
try {
await createUser(
values.firstName,
values.lastName,
values.email,
values.username,
credentials.user.uid,
);
} catch (error) {
addToast('error', 'Something went wrong!');
}
} catch (error) {
if (error.message.includes('auth/email-already-in-use')) {
formik.errors.email = 'This email is already in use!';
return;
}
addToast('error', 'Something went wrong');
}
addToast('success', 'You have been registered!');
addToast('success', 'Welcome to the UnHOOman Forum!');
navigate('/');
},
});
const resetFormHandler = () => {
formik.resetForm();
};
return (
<>
<label htmlFor="register-modal">
<p className="cursor-pointer">Register</p>
</label>
<input type="checkbox" id="register-modal" className="modal-toggle" />
<div className="modal modal-bottom sm:modal-middle">
<div className="modal-box min-h-1/2">
<div className="modal-action m-0 justify-center gap-2 h-full border-2 border-[#F79E47]">
<form onSubmit={formik.handleSubmit} className="w-full">
<div className='form-wrapper'>
<h1 className="font-bold">Register</h1>
<input
type="text"
placeholder="Username..."
name="username"
value={formik.values.username}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input w-full input-bordered border-[#F79E47] max-w-xs block"
/>
{formik.touched.username && formik.errors.username ? (
<p className="text-red-700 text-sm">{formik.errors.username}</p>
) : null}
<input
type="text"
placeholder="First name..."
name="firstName"
value={formik.values.firstName}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.firstName && formik.errors.firstName ? (
<p className="text-red-700 text-sm">{formik.errors.firstName}</p>
) : null}
<input
type="text"
placeholder="Last name..."
name="lastName"
value={formik.values.lastName}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.lastName && formik.errors.lastName ? (
<p className="text-red-700 text-sm">{formik.errors.lastName}</p>
) : null}
<input
type="email"
placeholder="Email..."
name="email"
value={formik.values.email}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.email && formik.errors.email ? (
<p className="text-red-700 text-sm">{formik.errors.email}</p>
) : null}
<input
type="password"
placeholder="Password..."
name="password"
value={formik.values.password}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.password && formik.errors.password ? (
<p className="text-red-700 text-sm">{formik.errors.password}</p>
) : null}
<div className="signup-buttons">
<label
type="button"
htmlFor="register-modal"
className="btn btn-outline"
onClick={() => resetFormHandler()}
>
CANCEL
</label>
<button type="submit" className="btn btn-outline">
Submit
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</>
);
}
2nd component from which I want to be able to trigger the modal
import {HOME_CARD_TITLE, HOME_CARD_DESC} from '../../common/constants.js';
import {useContext, useEffect, useState} from 'react';
import {getAllPosts} from '../../services/posts.service.js';
import {getAllUsers} from '../../services/user.request.services.js';
import AppContext from '../../context/AppContext.js';
import {Navigate, useNavigate} from 'react-router-dom';
export default function HomeInfoCard() {
const [cardState, setCardState] = useState({posts: 0, members: 0});
const {addToast} = useContext(AppContext);
const navigate = useNavigate();
useEffect(() => {
const updatePostsAndUsers = async () => {
const posts = (await getAllPosts()).length;
const members = (await getAllUsers()).length;
setCardState((prev) => ({
...prev,
posts,
members,
}));
};
updatePostsAndUsers().catch((error) => addToast('error', error.message));
}, []);
return (
<div className="card max-w-md bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title">{ HOME_CARD_TITLE }</h2>
<p className="mb-3">{ HOME_CARD_DESC }</p>
<div className="stats shadow text-center mb-3">
<div className="stat">
<div className="stat-title">Posts</div>
<div className="stat-value">{ cardState.posts }</div>
</div>
<div className="stat">
<div className="stat-title">Members</div>
<div className="stat-value">{ cardState.members }</div>
</div>
</div>
<div className="card-actions justify-center">
<button className="btn bg-[#FCDDBF] border-0 text-[#4A4846] hover:text-[#fff] w-full" onClick={() => navigate('/login')}>Join community</button>
</div>
</div>
</div>
);
}
I have two components, the first one is a register form with Formik and there is also a modal from daisyUI.
I have a 'View community button' in other component which I want to be able to make on click to open the register form modal from the other component, but I couldn't find a solution for this problem.
I've tried with data-toggle/data-target, but it didn't work.
The button which I want to be able to trigger the modal from the register component is in the end of the second component and it's currently leading to the home page with useNavigate.
I am not a professional programmer, and currently I am in a boot camp, so that's why the code is trashy, but we all start from somewhere. Thanks in advance for the replies!
I have been trying to display the form data submitted but the map is throwing an error.
I have two components
NameForm.js
Here is the form input, handlechange and handlesubmit methods are done
function Nameform() {
const [form, setForm] = useState({firstname: "", lastname: ""});
const handleChange = (e) => {
setForm({
...form,
[e.target.id]: (e.target.value),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("hello from handle submit", form );
}
return (
<section>
<div className='card pa-30'>
<form onSubmit={ handleSubmit }>
<div className='layout-column mb-15'>
<label htmlFor='name' className='mb-3'>First Name</label>
<input
type='text'
id='firstname'
placeholder='Enter Your First Name'
data-testid='nameInput'
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className='layout-column mb-15'>
<label htmlFor='name' className='mb-3'>First Name</label>
<input
type='text'
id='firstname'
placeholder='Enter Your First Name'
data-testid='nameInput'
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className='layout-row justify-content-end'>
<button
type='submit'
className='mx-0'
data-testid='addButton'
>
Add Name
</button>
</div>
</form>
</div>
</section>
)
}
export default Nameform
NameList.js
I want to pass the data in handleSubmit in NameForm.js to NameList.js. But the data is not displayed.
function NameList({form}) {
return (
<section>
{form.map(displayName => {
return (
<ul
className='styled w-100 pl-0'
>
<li
className='flex slide-up-fade-in justify-content-between'
>
<div className='layout-column w-40'>
<h3 className='my-3'>{displayName.firstname}</h3>
<p className='my-0'{displayName.lastname}></p>
</div>
</li>
</ul>
)
})}
</section>
)
}
export default NameList;
App.js
In App.js, I want to display both the form and the data.
import { Nameform, Namelist } from './components'
function App() {
return (
<div>
<div className='layout-row justify-content-center mt-100'>
<div className='w-30 mr-75'>
<Nameform />
</div>
<div className='layout-column w-30'>
<NameList />
</div>
</div>
</div>
)
}
export default App;
Thank you for your help!
Pass the data you want to share between parent and children via props (which stands for properties).
In the parent class, when rendering <NameForm> and <ListForm> add the data like that:
//if you want to share count and name for example:
<NameForm
count={this.state.count}
name={this.state.name}
/>
You can add as many props as you want. Furthermore, you can pass a function and its argument using arrow functions:
<NameForm
aFunction={() => this.myFunction( /* anArgument */ )}
/>
To access props in a child class dynamically wherever you need them:
{this.props.count}
{this.props.name}
{this.props.aFucntion}
You can get rid of this.props using a technique called object destructing:
render(
const {count, name, aFunction} = this.props;
//now you can use {count} and {name} and {aFunction} without this.props
);
There are some bugs in your code, first form is an object not an array, so you can't map it, you need to use form.firstname and form.lastname, Also you set both input ids equal firstname you need to modify it, Also you need to move the form state and handleChange function to the App component.
This is a working code of your example.
https://codesandbox.io/s/upbeat-forest-328bon
You can save the state in the parent component and pass it as props to the child components like so.
Here we make use of an outer state called submittedForm to display only the submitted values. The inner form state is being used for handling the values before submitting.
// App.js
function App() {
const [submittedForm, setSubmittedForm] = useState({
firstname: "",
lastname: "",
});
return (
<div>
<div className="layout-row justify-content-center mt-100">
<div className="w-30 mr-75">
<NameForm setSubmittedForm={setSubmittedForm} />
</div>
<div className="layout-column w-30">
<NameList form={submittedForm} />
</div>
</div>
</div>
);
}
export default App;
// NameForm.js
function NameForm({ setSubmittedForm }) {
const [form, setForm] = useState({
firstname: "",
lastname: "",
});
const handleChange = (e) => {
// setActive(true);
setForm({
...form,
[e.target.id]: e.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
setSubmittedForm(form);
};
return (
<section>
<div className="card pa-30">
<form onSubmit={handleSubmit}>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
First Name
</label>
<input
type="text"
id="firstname"
placeholder="Enter Your First Name"
data-testid="nameInput"
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
Last Name
</label>
<input
type="text"
id="lastname"
placeholder="Enter Your Last Name"
data-testid="nameInput"
value={form.lastname}
onChange={handleChange}
/>
</div>
<div className="layout-row justify-content-end">
<button type="submit" className="mx-0" data-testid="addButton">
Add Name
</button>
</div>
</form>
</div>
</section>
);
}
export default NameForm;
// NameList.js
function NameList({ form }) {
return (
<section>
<ul className="styled w-100 pl-0">
<li className="flex slide-up-fade-in justify-content-between">
<div className="layout-column w-40">
<h3 className="my-3">{form.firstname}</h3>
<p className="my-0">{form.lastname}</p>
</div>
</li>
</ul>
</section>
);
}
export default NameList;
Sign up Form Written in JSX
import './SignUp-Form-CSS.css'
import { Link } from 'react-router-dom';
import { useState, useContext } from 'react';
import AuthContext from '../Context_store/AuthContext/AuthContext';
const SignUp = () => {
const [name, setName] = useState(null);
const [password, setPassword] = useState(null);
const [confirmPassword, setConfirmPassword] = useState(null);
const [image, setImage] = useState(null);
const authCtx = useContext(AuthContext);
const nameChangeHandeler = (event) => {
setName(event.target.value)
}
const passwordChangeHandeler = (event) => {
setPassword(event.target.value)
}
const confirmPasswordChangeHandeler = (event) => {
setConfirmPassword(event.target.value);
}
const imageChangeHandeler = (event) => {
setImage(event.target.files[0]);
console.log(event.target.files[0]);
}
const onSubmitHandeler = (event) => {
event.preventDefault()
// const data = {
// username: name,
// password: password,
// confirmPassword: confirmPassword,
// image: image
// }
const data=new FormData();
data.append("name",name);
data.append("password",password);
data.append("confirmPassword",confirmPassword);
data.append("image",image);
// data.append('username',name);
console.log(data);
authCtx.signup(data)
}
return (
<div class="container">
<div class="row">
<div class="col-lg-10 col-xl-9 mx-auto">
<div class="card flex-row my-5 border-0 shadow rounded-3 overflow-hidden">
<div class="card-img-left d-none d-md-flex">
{/* <!-- Background image for card set in CSS! --> */}
</div>
<div class="card-body p-4 p-sm-5">
<h5 class="card-title text-center mb-5 fw-light fs-5">Register</h5>
<form onSubmit={onSubmitHandeler} encType='multipart/form-data' >
<div class="form-floating mb-3">
<input type="text" class="form-control"
id="floatingInputUsername"
onChange={nameChangeHandeler}
placeholder="myusername" required autofocus />
<label for="floatingInputUsername">Username</label>
</div>
{/* <div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingInputEmail" placeholder="name#example.com" />
<label for="floatingInputEmail">Email address</label>
</div> */}
<hr />
<div class="form-floating mb-3">
<input type="password"
class="form-control"
onChange={passwordChangeHandeler}
id="floatingPassword" placeholder="Password" />
<label for="floatingPassword">Password</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control"
onChange={confirmPasswordChangeHandeler}
id="floatingPasswordConfirm" placeholder="Confirm Password" />
<label for="floatingPasswordConfirm">Confirm Password</label>
</div>
<div>
<label>Select Logo </label>
<input name='image' onChange={imageChangeHandeler} type="file" class="form-control my-4" id="logo" placeholder="Select Logo " />
</div>
<div class="d-grid mb-2">
<button class="btn btn-lg btn-primary btn-login fw-bold text-uppercase" type="submit">Register</button>
</div>
<a class="d-block text-center mt-2 small" >Have an account?<Link class="nav-link" to={'/login'}> Sign In</Link></a>
<hr class="my-4" />
<div class="d-grid mb-2">
<button class="btn btn-lg btn-google btn-login fw-bold text-uppercase" type="submit">
<i class="fab fa-google me-2"></i> Sign up with Google
</button>
</div>
<div class="d-grid">
<button class="btn btn-lg btn-facebook btn-login fw-bold text-uppercase"
// onClick={onSubmitHandeler}
type="submit">
<i class="fab fa-facebook-f me-2"></i> Sign up with Facebook
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default SignUp;
CONTEXT API
import React, { useState } from "react";
import AuthContext from "./AuthContext";
const AuthContextProvider = (props) => {
const [token, setToken] = useState(null);
const login = (loginDetails) => {
}
const signUp = (signUpDetails) => {
console.log("Sign Up Called ");
fetch('http://localhost:5000/register',
{
method:'POST',
body:signUpDetails
// body:JSON.stringify(signUpDetails),
// headers:{
// 'Content-Type': 'application/json'
// }
}).then((resp) => {
return resp.json()
}).then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
}
const logout = () => {
}
const values = {
login: login,
signup: signUp,
logout: logout,
token: {
token: token,
setToken: setToken
},
isLoggedIn: !!token
}
return (
<div>
<AuthContext.Provider value={values}>
{props.children}
</AuthContext.Provider>
</div>
)
}
export default AuthContextProvider;
But When AT Node server End All Other filed Is recieved as Empty Except image Data
OUTPUT OF DATA SAVED IN DATABASE
And If I follow the Approach in Which I use Simple Object as the data instead of form Data(), I and with header ( that are under // ) I do not receive images at the backend and only receive user info
One solution could be to send the image as a string (base64) to the backend to save the image.
try to implement this in your react:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [img, setImg] = useState("Image string will come here");
const handleChangeImg = (e) => {
console.log(e.target.files[0]);
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onloadend = () => {
setImg(reader.result);
};
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<input onChange={handleChangeImg} type="file" name="image" />
<button onClick={()=>setImg("Image string will come here")}>Reset</button>
<h1>{img}</h1>
</div>
);
}
Check the code iin codesandbox here.
Related resources:
FileReader
I am working on a modal in which I have to access the id property of an HTML element to change its display property. I have used the useRef Hook to attain it but got the following errors.
TypeError: Cannot read property 'style' of null
Modal.js:- The code is of 1000 Lines so I am sharing only the part where I have used it.
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const addProjectModal = useRef(null);
const closeAddProjectModal = () => {
addProjectModal.current.style.display = 'block';
};
const renderAddProject = () => {
return (
<div>
<div className="task-accordion" ref={addProjectModal} style={{ display: 'none' }}>
<i
className="fa fa-close close-icon"
id="closeIconn"
onClick={() => closeAddProjectModal()}
aria-hidden="true"
></i>
<h4>ADD Project</h4>
<div className="form-group">
<textarea
id="textAreaAddProject"
type="text"
style={{ height: '30vh' }}
placeholder="Project Title"
name="Title"
className="form-control"
onChange={(e) => {
setprojectTitle(e.target.value);
}}
required
/>
</div>
<div className="col-sm-12 add-mupps-button">
<button type="submit" onClick={() => postProjectApiCall()}>
Save
</button>
</div>
</div>
</div>
);
};
<div className="col-sm-8 last-div padding-0">
{isAddProjectClicked && renderAddProject()}
</div>
You can change the visibility of your model by creating a state like this.
import { useState } from "react";
function App() {
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const [addProjectModal, openAddProjectModal] = useState("none"); // creating the state
const renderAddProject = () => {
return (
<div>
<div className="task-accordion" style={{ display: addProjectModal }}> {/* Set that as display's property value */}
<i
className="fa fa-close close-icon"
id="closeIconn"
onClick={() => openAddProjectModal("block")}
aria-hidden="true"
></i>
<h4>ADD Project</h4>
<div className="form-group">
<textarea
id="textAreaAddProject"
type="text"
style={{ height: "30vh" }}
placeholder="Project Title"
name="Title"
className="form-control"
required
/>
</div>
<div className="col-sm-12 add-mupps-button">
<button type="submit">Save</button>
</div>
</div>
</div>
);
};
return (
<>
{isAddProjectClicked && renderAddProject()}
<button onClick={() => openAddProjectModal("block")}>Click Me</button>
{/* Change the state using any event you want */}
</>
);
}
export default App;
Let me know if you need further support.
If the component is not mounted, the ref is null so you should add condition to it
const closeAddProjectModal = () => {
if(addProjectModal.current) {
addProjectModal.current.style.display = 'block';
}
};
or just use optional chaining
const closeAddProjectModal = () => {
addProjectModal.current?.style.display = 'block';
};
Use react to set the style:
function AddProjectModal(props) {
const [visible, setVisible] = useState(visible);
return <div style={{display: visible?'block':'none'}}>
<button onClick={()=>setVisible(false)}>
Close
</button>
</div>
}
No need for a ref to change the style or a class