Fetch all Names in Option Value, but getting only 1 - reactjs

I am getting array of objects on props.items. How to show all the name from props.items.name in option select? What is happening now is : I am only fetching Name for selected user but not getting all the users and select any choice. I am new to reactjs. Can anyone help me out yrr. Thanks in advance.
import { useEffect, useState } from 'react';
import { Button, Container, Row, Col, Form, Modal } from 'react-bootstrap';
import configData from './../../config/constant.json'
const TransferDialog = (props) => {
let [transferDiv, showtransferDiv] = useState(false)
let [deleteDiv, showDeleteDiv] = useState(false)
let [confirmDiv, showConfirmDiv] = useState(true);
let [error, setError] = useState(false);
let [pending, setPending] = useState(false);
let [permission, setPermission] = useState();
const [userDetails, setUserDetails] = useState([])
const [radio, setradio] = useState([])
let dropdownvalue;
useEffect(()=>{
console.log(props.items);
}, [])
function dropDown(value) {
dropdownvalue = value;
console.log("Dropdown", dropdownvalue);
}
function onChangeValue(event) {
console.log(radio);
}
function handleDelete() {
console.log("Id-->", props.userid);
const abortCont = new AbortController();
console.log("Dropdown", dropdownvalue, radio);
let data = {
id: dropdownvalue,
target: props.userid,
radio: radio
}
fetch(`${configData.SERVER_URL}/admin/deleteuser`, {
credentials: configData.COOKIES_CONFIG,
signal: abortCont.signal,
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
.then(res => {
if (!res.ok) {
throw Error('Something Went Wrong')
}
return res.json()
})
.then(res_data => {
if (res_data.status == false) {
throw Error(res_data.message)
} else {
setPending(false);
}
})
.catch(err => {
if (abortCont === "AbortError") {
} else {
setError(err.message);
setPending(false);
}
})
}
function handleYes() {
showConfirmDiv(false);
showDeleteDiv(true);
}
return (
<div>
<Modal
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
show={props.show}
onHide={props.onHide}
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Delete User
</Modal.Title>
</Modal.Header>
<Modal.Body>
{
confirmDiv
&&
<>
<h4>Do you really want to delete ??</h4>
</>
}
{
deleteDiv &&
<div onChange={onChangeValue}>
<p>Do you want to transfer user data to another user??</p>
<input type="radio" value="Yes" name="Yes" onChange={() => showtransferDiv(true) || setradio(true)} />
<label >Yes</label>
<input type="radio" value="No" name="Yes" onChange={() => showtransferDiv(false) || setradio(false) } />
<label >No</label><br />
</div>
}
{
transferDiv &&
<Form>
<Form.Group className="mb-3">
<Row>
<Col>
<Form.Group className="mb-3">
</Form.Group>
</Col>
<Col>
<Form.Select onChange={(e) => dropDown(e.target.value)}>
<option value={props.items._id} >{props.items.name}</option>
</Form.Select>
</Col>
</Row>
</Form.Group>
<Form.Text className="text-muted">
</Form.Text>
</Form>
}
{
pending && <p>Pending...</p>
}
{
error && <p>{error}</p>
}
</Modal.Body>
<Modal.Footer>
{
confirmDiv &&
<>
<Button onClick={handleYes}>Yes</Button>
<Button onClick={props.onHide}>No</Button>
</>
}
{
deleteDiv &&
<>
<Button onClick={props.onHide}>Cancel</Button>
<Button onClick={handleDelete}>Delete</Button>
</>
}
</Modal.Footer>
</Modal>
</div>
);
}
export default TransferDialog;

If I understood well, you can try to replace
<option value={props.items._id} >{props.items.name}</option>
with this
{
props.items.map((element) => <option value={element._id} >{element.name}</option>)
}
You can also try this as it is better
{
renderOptions(props.items)
}
While renderOptions looks like this
function renderOptions(items) => items.map((item) => <option value={item._id} >{item.name}</option>)

Not getting clearly but your finding is to be iterate items on object entities. For that if we condition following aspects could solve the problem.
1 If props.items is array then you only need to use map above the tag
2 If props.items is object then you can use forin loop to iterate and get the named fields and assigned in array state, then use map on the tag.

Related

i have problem with writing Page in react js?

I can't type in the fields for a page Login on React js and mdbootstrap and css
this is the problem please my developper Help me solve this problem I have suffered with it a lot What is the solution please help me with this problem
What is the root of this problem please fix my code
This is the problem code
import React, { useState } from 'react';
export default function App() {
const [iconsActive, setIconsActive] = useState('pill1');
const handleIconsClick = (value: string) => {
if (value === iconsActive) {
return;
}
setIconsActive(value);
};
const [values, setValues] = useState({
email: '',
password: '',
});
const [submitted, setSubmitted] = useState(false);
const [showSuccess, setShowSuccess] = useState(false);
const handleInputChange = (event) => {
event.persist();
setValues((values) => ({
...values,
[event.target.email]: event.target.value,
}));
};
const isFormValid = () => {
if (!values.email || !values.password) {
return false;
} else {
return true;
}
};
const handleSubmit = (e) => {
e.preventDefault();
setSubmitted(true);
if (isFormValid()) {
setShowSuccess(true);
}
};
return (
<div
className='text-center'
id='formm'
class='register-form'
onSubmit={handleSubmit}
>
{showSuccess && (
<div class='success-message'>Success! Thank you for registering</div>
)}
<MDBTabs pills justify className='mb-3'>
<MDBCol>
<MDBTabsItem>
<MDBTabsLink
onClick={() => handleIconsClick('pill1')}
active={iconsActive === 'pill1'}
>
<MDBIcon className='me-2' />
Login
</MDBTabsLink>
</MDBTabsItem>
</MDBCol>
<MDBTabsItem>
<MDBTabsLink
onClick={() => handleIconsClick('pill2')}
active={iconsActive === 'pill2'}
>
<MDBIcon className='me-2' />
Register
</MDBTabsLink>
</MDBTabsItem>
</MDBTabs>
<MDBTabsContent>
<MDBTabsPane show={iconsActive === 'pill1'}>
<MDBInput
className='mb-4'
type='email'
id='form7Example1'
label='Email address'
disabled={showSuccess}
value={values.email}
onChange={handleInputChange}
/>
{submitted && !values.email && (
<span id='email-error'>Please enter a email</span>
)}
<MDBInput
className='mb-4'
type='password'
id='form7Example2'
label='Password'
disabled={showSuccess}
value={values.password}
onChange={handleInputChange}
/>
{submitted && !values.password && (
<span id='password-error'>Please enter a last name</span>
)}
<MDBBtn type='submit' className='mb-4' block disabled={showSuccess}>
Sign in
</MDBBtn>
Your problem is here:
setValues((values) => ({
...values,
[event.target.email]: event.target.value,
}));
You are setting value to the wrong keys.
I would suggest you to create two states and separate handlers for every input OR you can do the following:
const handleInputChange = (type) => ({target}) => {
setValues((values) => ({
...values,
[type]: target.value,
}));
};
<MDBInput
value={values.email}
onChange={handleInputChange("email")}
/>
<MDBInput
value={values.password}
onChange={handleInputChange("password")}
/>

Selecting all check boxes of one group when select all is clicked in React

I am showing permissions for my project in an accordion which whose title is the group from which the permission belongs, I have put select all checkbox in each accordion now I want to check the select all checkbox and all the check boxes in that accordion only gets selected and their ids being pushed in the array which will be then passed to PHP API. Here is the code:
const Add_AssignPermission = () => {
const [permissionData, setPermissionData] = useState([])
const [roleData, setRoleData] = useState([])
const [selectedRole, setSelectedRole] = useState('')
const [selectedPermission, setSelectedPermission] = useState('')
const [checkPermission, setCheckedPermission] = useState([])
const { role_id } = useParams()
function getPermissionData() {
const url = '/permission?length=10000'
axios.get(url)
.then((response) => {
console.log(response)
setPermissionData(response.data.data.data)
setSelectedPermission(response.data.data.data[0].id)
})
.catch((err) => console.log(err))
}
function getRoleData() {
const url = '/roles'
axios.get(url)
.then((response) => {
console.log(response)
setRoleData(response.data.data.data)
setSelectedRole(response.data.data.data[0].id)
})
.catch((err) => console.log(err))
}
const [successMessage, setSuccessMessage] = useState(null)
useEffect(() => {
getPermissionData()
getRoleData()
}, [])
useEffect(() => {
window.scrollTo(0, 0)
}, [successMessage])
const resetForm = () => {
setPermissionData("")
setRoleData("")
}
function submit(event, errors, values) {
event.preventDefault()
if (errors.length < 1) {
const body = {
checkedPermission: checkPermission
}
const config = {
method: 'post',
url: `/assignPermissiontoRole/${role_id}`,
headers: AuthHeader(),
data: body
}
console.log(config)
axios(config)
// const response = axios.post(`/assignPermissiontoRole/${role_id}`, checkPermission)
// console.log(response)
.then(function (response) {
console.log(response)
setSuccessMessage("Permissions Assigned Successfully")
getPermissionData()
getRoleData()
})
.catch(function (error) {
console.log(error)
setSuccessMessage(null)
})
}
}
return (
<>
<Card>
<CardHeader>
<CardTitle tag="h4">Assign A New Permission to Role</CardTitle>
</CardHeader>
{/* {successMessage && <Alert> {successMessage}</Alert>}
<AvForm
onSubmit={(event, errors, values) => submit(event, errors, values)
}
>
<Row>
<Col sm ='12' md='6'>
<Row>
<Col sm='6'>
<AvGroup>
<Label> Permission Name </Label>
<AvInput
name="permission"
id="permission"
type = "select"
onChange={(e) => setSelectedPermission(e.target.value)}
required
value = {selectedPermission}
>
{permissionData.map((value, key) => {
// let permissionName = properCase(value.name, '-')
// permissionName = properCase(permissionName, '_')
return (
<option value={value.id} key={key}>
{value.name}
</option>
)
})}
</AvInput>
<AvFeedback>Please enter a Permission!</AvFeedback>
</AvGroup>
</Col>
<Col sm='6'>
<AvGroup>
<Label> Role Name </Label>
<AvInput
name="role"
id="role"
type = "select"
onChange={(e) => setSelectedRole(e.target.value)}
required
value = {selectedRole}
>
{roleData.map((value, key) => {
const roleName = properCase(value.name, '_')
return (
<option value={value.id} key={key}>
{roleName}
</option>
)
})}
</AvInput>
<AvFeedback>Please enter a Role!</AvFeedback>
</AvGroup>
</Col>
</Row>
</Col>
<Col sm ='12' md='6' className = 'align-self-center mt-75'>
<AvGroup className="d-flex mb-0">
<Button.Ripple
className="mr-1"
color="primary"
type="submit"
>
Submit
</Button.Ripple>
<Button outline color='secondary' onClick={resetForm}>
Reset
</Button>
</AvGroup>
</Col>
</Row>
</AvForm> */}
{successMessage && <Alert> {successMessage}</Alert>}
</Card>
<Row>
<Col sm="12">
<AvForm onSubmit={(event, errors, values) => submit(event, errors, values)} >
{permissionData.map((ele) => {
return (
<Accordion title={ele.group_name} Open={true} key={ele.group_name}>
<Row>
<Col sm='3'>
<AvGroup>
<AvInput type='checkbox' name='select_all_checkbox' id='select_all_checkbox'
//onChange={handleChange}
onChange={(e) => {
if (e.target.checked) {
ele.grouped_permissions.map((permissions) => {
setCheckedPermission([
...checkPermission,
{
id: permissions.permission_id
}
])
})
console.log(checkPermission)
} else {
console.log("IN ELSE")
}
}}
/>
{console.log(checkPermission)}
<Label for='decimal_point_in_valueVertical'>Select All</Label>
</AvGroup>
{/* <AvGroup>
<AvInput type='checkbox' name='select_all_checkbox' id='select_all_checkbox'
//onChange={handleChange}
onChange={(e) => {
if (e.target.checked) {
ele.grouped_permissions.map((permissions) => {
setCheckedPermission([
...checkPermission,
{
id: permissions.permission_id
}
])
})
console.log(checkPermission)
} else {
console.log("IN ELSE")
}
}}
/>
{console.log(checkPermission)}
<Label for='decimal_point_in_valueVertical'>Select All</Label>
</AvGroup> */}
</Col>
{ele.grouped_permissions.map((ele2) => {
return (
<Col sm='3' key={ele2.permissions.name}>
<AvGroup>
<AvInput type='checkbox' name='permission_checkbox' id='permission_checkbox'
//key={ele2.permissions.name}
onChange={(e) => {
console.log(e.target)
if (e.target.checked) {
setCheckedPermission([
...checkPermission,
{
// id: ele2.permissions.id
id: ele2.permissions.id
}
])
//console.log(checkPermission)
} else {
setCheckedPermission(
checkPermission.filter((checkedBox) => checkedBox.id !== ele2.permissions.id)
)
}
}}
data={ele2.permissions.id}
value={checkPermission}
/>
<Label for='decimal_point_in_valueVertical'>{ele2.permissions.name}</Label>
{/* <AvInput type='text' name='mask_name' id='mask_name' placeholder='mask_name'
/> */}
</AvGroup>
</Col>
)
})}
</Row>
</Accordion>
)
})}
<Col sm='12'>
<AvGroup className='d-flex mb-0 float-right'>
<Button.Ripple className='mr-1' color='primary' type='submit' >
Assign Permission
</Button.Ripple>
</AvGroup>
</Col>
</AvForm>
</Col>
</Row>
</>
)
}
export default Add_AssignPermission
I have tried multiple things but nothing worked for me. In the above code snippet I was trying to push that group values into the state using map but when I consoled it contains only the last permission id in the accordion. PLEASE HELP !!!!

Can't update the profile picture using cloudinary

I've tried to upload an image in Cloudinary and also want to save it into my database. Uploading an image in Cloudinary works fine but I can't save it into my database. Whenever I tried to do this it's only using the default image I've set in my model. Also likes setPic is working but for a moment and again it's changed to the default image. Please anybody help me figure out this problem.
Please comment if any other details if you need. Please help me.
Here is the Function
const postDetails = (pics) => {
setPicMessage(null);
if (pics?.type === 'image/jpeg' || pics?.type === 'image/png') {
const data = new FormData();
data.append('file', pics);
data.append('upload_preset', 'codeblogger_profile_image');
data.append('cloud_name', 'dhuej17x0');
fetch('https://api.cloudinary.com/v1_1/dhuej17x0/image/upload', {
method: 'post',
body: data,
})
.then((res) => res.json())
.then((data) => {
setPic(data.secure_url.toString());
console.log(pic);
})
.catch((err) => {
toast.error(err);
});
} else {
setPicMessage('Please Select an Image');
toast.error(picMessage);
}
};
And here is the full Profile.js File
import React, { useEffect, useState } from 'react';
import { Button, Col, Container, Form, InputGroup, Row } from 'react-bootstrap';
import { toast, ToastContainer } from 'react-toastify';
import { useDispatch, useSelector } from 'react-redux';
import { getUserDetails, updateUserProfile } from '../actions/userActions';
import { USER_UPDATE_PROFILE_RESET } from '../constant/userConstants';
const Profile = ({ history }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [pic, setPic] = useState();
const [password, setPassword] = useState('');
const [picMessage, setPicMessage] = useState();
const [confirmPassword, setConfirmPassword] = useState('');
const [passwordType, setPasswordType] = useState('password');
const [passwordType2, setPasswordType2] = useState('password');
const [showPass, setShowPass] = useState(false);
const [showPass2, setShowPass2] = useState(false);
const dispatch = useDispatch();
const userDetails = useSelector((state) => state.userDetails);
const { user } = userDetails;
// console.log(` this is from line 25 ${user}`);
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
const userUpdateProfile = useSelector((state) => state.userUpdateProfile);
const { success } = userUpdateProfile;
useEffect(() => {
if (!userInfo) {
history.push('/login');
} else {
if (!user || !user.name || success) {
dispatch({ type: USER_UPDATE_PROFILE_RESET });
dispatch(getUserDetails('profile'));
} else {
setName(user.name);
setEmail(user.email);
setPic(user.pic);
}
}
if (success) {
toast.success('Profile Updated successfully');
}
showPass ? setPasswordType('text') : setPasswordType('password');
showPass2 ? setPasswordType2('text') : setPasswordType2('password');
}, [showPass, showPass2, dispatch, history, success, user, userInfo]);
const postDetails = (pics) => {
setPicMessage(null);
if (pics?.type === 'image/jpeg' || pics?.type === 'image/png') {
const data = new FormData();
data.append('file', pics);
data.append('upload_preset', 'codeblogger_profile_image');
data.append('cloud_name', 'dhuej17x0');
fetch('https://api.cloudinary.com/v1_1/dhuej17x0/image/upload', {
method: 'post',
body: data,
})
.then((res) => res.json())
.then((data) => {
setPic(data.secure_url.toString());
console.log(pic);
})
.catch((err) => {
toast.error(err);
});
} else {
setPicMessage('Please Select an Image');
toast.error(picMessage);
}
};
const submitHandler = (e) => {
e.preventDefault();
if (password !== confirmPassword) {
toast.error('Passwords do not match');
} else {
dispatch(updateUserProfile({ id: user._id, name, email, password }));
}
};
return (
<div className="profilePage mt-4 py-3">
<ToastContainer />
<Container>
<h2>PROFILE</h2>
<Row className="profileContainer">
<Col md={6}>
<Form onSubmit={submitHandler}>
<Form.Group controlId="name" className="mb-2">
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
value={name}
placeholder="Name"
onChange={(e) => setName(e.target.value)}
/>
</Form.Group>
<Form.Group controlId="email" className="mb-2">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group controlId="password" className="mb-2">
<Form.Label>New Password</Form.Label>
<InputGroup>
<Form.Control
type={passwordType}
placeholder="New Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<InputGroup.Text>
<i
onClick={() => setShowPass(!showPass)}
className={showPass ? 'fas fa-eye-slash' : 'fas fa-eye'}
style={{ cursor: 'pointer' }}></i>
</InputGroup.Text>
</InputGroup>
</Form.Group>
<Form.Group controlId="confirmPassword" className="mb-2">
<Form.Label>Confirm Password</Form.Label>
<InputGroup>
<Form.Control
type={passwordType2}
placeholder="Confirm Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<InputGroup.Text>
<i
onClick={() => setShowPass2(!showPass2)}
className={showPass2 ? 'fas fa-eye-slash' : 'fas fa-eye'}
style={{ cursor: 'pointer' }}></i>
</InputGroup.Text>
</InputGroup>
</Form.Group>
<Form.Group controlId="pic" className="mb-2">
<Form.Label>Change Profile Picture</Form.Label>
<Form.Control
onChange={(e) => postDetails(e.target.files[0])}
type="file"
accept=".jpeg,.png,.jpg"
custom="true"
/>
</Form.Group>
<Button
type="submit"
variant="success"
style={{ letterSpacing: '2px' }}>
UPDATE
</Button>
</Form>
</Col>
<Col
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<img src={pic} alt={user.name} className="profilePic" />
</Col>
</Row>
</Container>
</div>
);
};
export default Profile;
In submitHandler method, you are not passing pic variable in updateUserProfile.
use this
dispatch(updateUserProfile({ id: user._id, name, email, password, pic }));

Redux form, loses focus after keypress?

Im experiencing a bit of a weird problem.
I have a form where I have an input field, but after i trigger an onChange event with a keypress it loses focus from the input field...
{editPhoneEnabled && <Field
name="phoneNumber"
component={phoneFieldRenderer}
validate={[Validator.required, Validator.phone]}
/>}
const phoneFieldRenderer = ({ input }) => {
return (
<ReactTelInput {...input} />
)
}
I've seen this example setup where the problem occurs (https://codepen.io/invisiblecomma/pen/wqLaZQ), but i've done the similar as you can see, but it still doesnt work?
Any ideas out there to help me understand what is going on?
The whole component:
import React, { useState, useEffect } from 'react';
import { Field, reduxForm, getFormValues } from 'redux-form';
import { Col, FormGroup } from 'reactstrap';
import { t } from 'Utilities/i18n';
import Validator from 'Utilities/validation';
import Button from 'Components/Forms/Button';
import { connect } from 'react-redux';
import { gql, useMutation } from '#apollo/client';
import ReactTelInput from 'react-telephone-input';
const formName = 'PhoneVerifyForm';
const performPhoneVerificationMutation = gql`
mutation registerPage_userPhoneVerification($input: RegisterPhoneVerificationInput!) {
userPhoneVerification(input: $input) {
errors {
field
messages
}
phoneNumberVerificationSid
verified
}
}
`;
// TODO props: unconfirmedUserId, phoneNumberVerificationSid, initialValues, callback
const PhoneVerifyForm = (props) => {
const [editPhoneEnabled, setEditPhoneEnabled] = useState(false);
const [codeSend, setCodeSend] = useState(props.phoneNumberVerificationSid !== undefined);
const [performPhoneVerification, { data:performPhoneVerificationData }] = useMutation(performPhoneVerificationMutation);
const [errors, setErrors] = useState([]);
useEffect(() => {
if (performPhoneVerificationData && performPhoneVerificationData.userPhoneVerification) {
const {userPhoneVerification} = performPhoneVerificationData;
if(userPhoneVerification.errors.length === 0) {
setErrors([])
if (editPhoneEnabled) {
editPhoneEnabled(false);
}
setCodeSend(userPhoneVerification.phoneNumberVerificationSid !== undefined);
if(userPhoneVerification.verified !== undefined) {
props.callback(props.formValues.phone);
}
} else {
setErrors(userPhoneVerification.errors)
}
}
}, [performPhoneVerificationData, ])
function handleSubmit(values) {
if (editPhoneEnabled) {
// update phone number
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
phone: values.phoneNumber,
},
},
});
} else if (!codeSend) {
// send new code
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
channel: values.channel,
},
},
});
}
// else validate code
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
code: values.code,
},
},
});
}
const handleEditPhone = () => {
setEditPhoneEnabled(!editPhoneEnabled);
setCodeSend(false);
};
const phoneFieldRenderer = ({ input }) => {
return (
<ReactTelInput {...input} />
)
}
return (
<form className="row" onSubmit={props.handleSubmit(handleSubmit)}>
{!codeSend && <Col style={{background: 'pink'}}>
<p>{t('select channel')}</p>
<FormGroup row className="indented-form-group">
<Col>
<label>
<Field
name="channel"
component="input"
type="radio"
value="sms"
validate={Validator.required}
/>
{t('Sms')}
</label>
</Col>
<Col xs={6}>
<label>
<Field
name="channel"
component="input"
type="radio"
value="phone"
validate={Validator.required}
/>
{t('phone')}
</label>
</Col>
</FormGroup>
</Col>}
{codeSend && <Col style={{background: 'yellow'}}>
<FormGroup row className="indented-form-group">
<Field
labelClassname="required"
label={t('Code')}
name="code"
component="input"
type="text"
validate={[Validator.required]}
/>
</FormGroup>
</Col>}
<Col style={{background: 'red'}}>
<FormGroup row className="indented-form-group">
{!editPhoneEnabled && <div>
<span>PHONE PLACEHOLDER</span><br />
<span onClick={handleEditPhone}>Edit phone number</span>
</div>}
{editPhoneEnabled && <Field
name="phoneNumber"
component={phoneFieldRenderer}
validate={[Validator.required, Validator.phone]}
/>}
</FormGroup>
</Col>
<Col>
<FormGroup row className="indented-form-group">
<Button submit disabled={props.submitting || props.invalid}>
{editPhoneEnabled ? t('Change phone number') : codeSend ? t('Validate code') : t('Send code')}
</Button>
</FormGroup>
</Col>
</form>
);
};
const mapStateToProps = state => ({
formValues: getFormValues(formName)(state) || {}, //This basically gives us the form values in the props and it gets updated on keydown.
});
const decoratedComponent = connect(mapStateToProps, null)(PhoneVerifyForm)
export default (reduxForm({
form: formName,
enableReinitialize: true,
shouldAsyncValidate: ({ trigger }) => ['blur'].includes(trigger),
})(decoratedComponent));

Page not re-rendering after hook state update

The page renders the input correctly, however errors only seem to appear on the second render even though they are displayed is useForm state on the first render.
So for the password field I enter a single character and the state of useForm changes to {password: "Must be at.....} but the screen does not update to display errors.password until I enter in another character.
// nodejs library that concatenates classes
// reactstrap components
import {
Button,
Card,
CardBody,
CardHeader,
Col,
Container,
Form,
FormFeedback,
FormGroup,
Input,
Row
} from "reactstrap";
import {register} from "apiCalls/AuthRequests";
import useForm from "hooks/AuthHooks";
import {registerFormValidation} from "components/validation/AuthFormValidation";
function RegisterForm(props) {
const [loading, setLoading] = useState(false);
const {values, handleChange, handleSubmit, errors, setErrors} = useForm(submit, registerFormValidation);
const emailRef = useRef(null);
const passwordRef = useRef(null);
const accessCodeRef = useRef(null);
async function submit() {
setLoading(true);
const response = await register(values.email, values.password, values.accessCode);
if (response.ok) {
} else if (response.status === 422) {
if ("access_code" in response.data) {
accessCodeRef.current.focus();
setErrors({accessCode: response.data.access_code});
}
if ("email" in response.data) {
setErrors({email: response.data.email});
emailRef.current.focus();
}
if ("password" in response.data) {
setErrors({password: response.data.password});
passwordRef.current.focus();
}
}
setLoading(false)
}
useEffect(() => {
console.log(errors);
});
return (
<>
<div className="content">
<Container className="pb-5">
<Row>
<Col lg="6" md="8" className="ml-auto mr-auto">
<Card className="bg-secondary border-0">
<CardHeader className="bg-transparent">
<div className="text-center">
<h2>Register</h2>
</div>
</CardHeader>
<CardBody className="px-lg-5 py-lg-5">
<Form role="form" onSubmit={handleSubmit}>
<FormGroup>
<label
className="form-control-label"
>
Email
</label>
<Input
name="email"
type="email"
innerRef={emailRef}
value={values.email || ""}
onChange={handleChange}
invalid={!!errors.email}
required
/>
<FormFeedback>{errors.email}</FormFeedback>
</FormGroup>
<FormGroup>
<label
className="form-control-label"
>
Password
</label>
<Input
name="password"
type="password"
innerRef={passwordRef}
value={values.password || ""}
onChange={handleChange}
invalid={!!errors.password}
required
/>
<FormFeedback>{errors.password}</FormFeedback>
</FormGroup>
<FormGroup>
<label
className="form-control-label"
>
Access Code
</label>
<Input
name="accessCode"
type="text"
innerRef={accessCodeRef}
value={values.accessCode || ''}
onChange={handleChange}
invalid={!!errors.accessCode}
required
/>
<FormFeedback>{errors.accessCode}</FormFeedback>
</FormGroup>
<Row className="my-4">
<Col xs="12">
<div
className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id="customCheckRegister"
type="checkbox"
required
/>
<label
className="custom-control-label"
htmlFor="customCheckRegister"
>
<span className="text-muted">
I agree with the{" "}
<a
href=""
target="_blank"
rel="noopener noreferrer"
>
Privacy Policy
</a>
</span>
</label>
</div>
</Col>
</Row>
<div className="text-center">
<Button disabled={loading} className="mt-4" color="info" type="submit">
Create account
</Button>
</div>
</Form>
</CardBody>
</Card>
</Col>
<Col md="4" className="ml-auto mr-auto">
<h2>Being a photographer is easier with <b className="text-primary">FOCAL</b></h2>
<ul>
<li>
<h4>Focal is great</h4>
</li>
<li>
<h4>Save time</h4>
</li>
<li>
<h4>More customers</h4>
</li>
</ul>
</Col>
</Row>
</Container>
</div>
</>
);
}
export default RegisterForm;
const useForm = (callback, validate) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = (event) => {
if (event) event.preventDefault();
setIsSubmitting(true);
};
useEffect(() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
setIsSubmitting(false);
}
}, [callback, errors, isSubmitting]);
useEffect(() => {
setErrors(validate(values, errors));
}, [validate, values, errors]);
const handleChange = (event) => {
event.persist();
setValues(values => ({...values, [event.target.name]: event.target.value}));
setErrors(validate(values, errors));
};
return {
handleChange,
handleSubmit,
setErrors,
values,
errors
}
};
export default useForm;
export function registerFormValidation(values, errors) {
if (values.email === ""){
delete errors.email;
}
if (values.password) {
if (!verifyLength(values.password, PASSWORD_LENGTH)){
errors.password = "Password must be greater than 8 Characters";
} else {
delete errors.password;
}
}
if (values.accessCode === "") {
delete values.accessCode;
}
return errors
}```
I can appreciate that it's of interest to work on a custom hook but forms are ubiquitous and there are solid, proven means to work with them. IMHO Formik is probably the best.
With the three fields that you have there, you could implement something like the following:
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<Formik
initialValues={{ email: '', password: '', accessCode: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={props => (
<form onSubmit={props.handleSubmit}>
<input
type="email"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
{props.errors.email && <div id="feedback">{props.errors.email}</div>}
<input
type="password"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.password}
name="password"
/>
{props.errors.password && <div id="feedback">{props.errors.password}</div>}
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.accessCode}
name="accessCode"
/>
{props.errors.accessCode && <div id="feedback">{props.errors.acessCode}</div>}
<button type="submit">Submit</button>
</form>
)}
/>
</div>
);
Note that the above is just from memory - which to some extent speaks to it's simplicity. As initialValues are supplied to Formik, you can build complex component hierarchies without having to pass down form state but that's just one of the various benefits.
I have an assumption.
In registerFormValidation doesn't modify errors object. Create a new each time.
FIXED:
const useForm = (callback, validate) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const handleSubmit = (event) => {
if (event) event.preventDefault();
if (Object.keys(errors).length === 0){
callback();
}
};
const handleChange = (event) => {
event.persist();
setValues(values => ({...values, [event.target.name]: event.target.value}));
setErrors(validate({[event.target.name]: event.target.value}, errors));
};
return {
handleChange,
handleSubmit,
setErrors,
values,
errors
}
};
export default useForm;
got rid of useEffect!

Resources