Description
i'm working on reactjs and apollo client project. i'm also developing editUser feature. edit mutation is working but table UI data not update. i want to update data in the table after Edit Mutation.
Problem
So here , im edit this user data.
into this.
when i'm click "submit" the data is still not update UI.
but when i'm refreshing the page
the data shows correctly edit and changed.
Code
ModalEdit.js
import React, { useEffect, useState } from "react";
import { useQuery, useMutation } from "#apollo/client";
import { Button, Modal, Form } from "react-bootstrap";
import { GET_ALL_USERS, GET_USER_BY_ID } from "../../../gql/query";
import { UPDATE_USER } from "../../../gql/mutation";
const ModalEdit = ({ show, onClose, username }) => {
// state for check input component
const [isChecked, setIsChecked] = useState("ACTIVE");
// state for input values
const [value, setValue] = useState({
group_id: "",
full_name: "",
email: "",
phone: "",
address: "",
password: "",
});
useEffect(() => {
if (show) {
document.body.classList.add("modal-open");
}
return () => {
if (document.body.classList.contains("modal-open")) {
document.body.classList.remove("modal-open");
}
};
}, [show]);
const { data, loading, error, refetch } = useQuery(GET_USER_BY_ID, {
variables: { username: username },
});
const [updateUser, { error: updateError, loading: updateLoading }] =
useMutation(UPDATE_USER, {
onCompleted: () => {
refetch();
onClose();
},
onError: (err) => {
console.error(JSON.stringify(err, null, 2));
},
});
const dataUser = data?.getUserByID;
useEffect(() => {
if (dataUser) {
setValue({
group_id: dataUser.group_id,
full_name: dataUser.full_name,
email: dataUser.email,
phone: dataUser.phone,
address: dataUser.address,
password: dataUser.password,
});
}
}, [dataUser]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
const handleChange = (event) => {
setValue({ ...value, [event.target.name]: event.target.value });
};
// handle mutation for edit user
const handleSubmit = (event) => {
event.preventDefault();
updateUser({
variables: {
username: username,
input: {
group_id: value.group_id,
full_name: value.full_name,
email: value.email,
phone: value.phone,
address: value.address,
password: value.password,
},
}
});
};
return (
<Modal show={show}>
<Modal.Header>
<Modal.Title>
{" "}
<span>FORMULIR AKUN PENGGUNA</span>{" "}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3">
<Form.Label>Role Akun</Form.Label>
<Form.Select
aria-label="pilih user role"
name="group_id"
onChange={handleChange}
>
<option value={value.group_id}>{value.group_id}</option>
</Form.Select>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Nama Lengkap</Form.Label>
<Form.Control
type="text"
name="full_name"
value={value.full_name}
onChange={handleChange}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
name="email"
value={value.email}
onChange={handleChange}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Phone</Form.Label>
<Form.Control
type="text"
name="phone"
value={value.phone}
onChange={handleChange}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
name="password"
value={value.password}
onChange={handleChange}
/>
</Form.Group>
<Form.Label>Aktifkan Akun</Form.Label>
{dataUser.status === "ACTIVE" ? (
<Form.Check
type="switch"
checked={isChecked}
onChange={(event) => setIsChecked(event.target.checked)}
id="custom-switch"
label="Aktifkan Akun"
/>
) : (
<Form.Check
type="switch"
id="custom-switch"
checked={isChecked}
onChange={(event) => setIsChecked(event.target.checked)}
label="Aktifkan Akun"
/>
)}
<Form.Group className="d-flex">
<Button variant="primary" type="submit">
Submit
</Button>
<Button variant="secondary" onClick={onClose}>
Close
</Button>
</Form.Group>
</Form>
</Modal.Body>
</Modal>
);
};
export default ModalEdit;
Table.js (Modal Edit Triggerred From Here)
<Table responsive>
<thead>
<tr>
<th>No</th>
<th>Nama Pengguna</th>
<th>Role Akun</th>
<th>Email</th>
<th>Tanggal Daftar</th>
<th>Status Akun</th>
<th>Pengaturan</th>
</tr>
</thead>
<tbody>
{data.getAllUsers.rows.map((user, index) => (
<tr key={user.username}>
<td>{index + 1}</td>
<td>{user.full_name}</td>
<td>{user.group_id}</td>
<td>{user.email}</td>
<td>{user.created_dtm}</td>
<td>{user.status}</td>
<td>
{user.status !== "ACTIVE"
? [
<Button
key="Aktifkan Akun"
className="d-block mb-2 text-white bg-secondary w-100"
>
Aktifkan Akun
</Button>,
<Button
key="Ganti Role Akun"
className="d-block mb-2 btn-block btn-sm w-100"
disabled
>
Ganti Role Akun
</Button>,
]
: user.group_id === "admin"
? [
<Button
key="Ganti Role Akun"
variant="outline-success"
className="d-block btn-sm mb-2 w-100"
>
Ganti Role Akun
</Button>,
]
: [
<Button
key="Pilih Role Akun"
className="d-block btn-sm mb-2 w-100"
>
Pilih Role Akun
</Button>,
]}
<Button
key="Edit"
variant="outline-primary"
onClick={() => {
setShowModal(true);
setUsername(user.username);
}}
className="d-block btn-block btn-sm mb-2 w-100"
>
Edit
</Button>
<Button
key="Hapus"
variant="outline-danger"
onClick={() => {
handleDelete(user.username)
}}
disabled={loading}
className="d-block btn-block btn-sm mb-2 w-100"
>
Hapus
</Button>
</td>
</tr>
))}
</tbody>
{showModal ? (
<ModalEdit
show={handleShowModal}
onClose={handleCloseModal}
username={username}
/>
) : null}
</Table>
Question
How to make the UI change after Edit Mutation ?
Any help will be appreciated , Thankyou
Related
Hello on ReactJS Bootstrap. When I try to click my button for the handleShow of the Modal
It sends me to a blank webpage and just get's stuck there. I tried to debug and my state show changes to true but it never shows the modal.
added text since stack overflow does not want me to post if the wording is not a lot, added text since stack overflow does not want me to post, added text since stack overflow does not want me to post
Any help would be appreciated.
Thanks!
import React, { Component, useState } from "react";
import Modal from "react-bootstrap/Modal";
import { useHistory } from "react-router-dom";
import Axios from "axios";
// import Profile from '../pages/forms';
import { Link } from "react-router-dom";
import Form from "react-bootstrap/Form";
import "../index.css";
import { setGlobalState, useGlobalState } from "../globals/globalVar";
function LoginComponent(props) {
// const
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [show, setShow] = useState(false);
const history = useHistory();
// make an axios query
const url = useGlobalState("defaultUrl");
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const handleEmailAndpasswordVerify = () => {
if (email.length === 0) {
alert("Email cannot be empty.");
return -1;
}
// email must contain #csub.edu
if (!email.includes("#csub.edu")) {
alert("Email must contain #csub.edu");
return -1;
}
// if the len of password is less than 8 then reject
if (password.length < 8) {
alert("Password must be at least 8 characters long.");
return -1;
}
return 0;
};
const handleSubmit = (e) => {
if (handleEmailAndpasswordVerify() !== 0) {
// if the email and password are valid then send the request to the server
// to register
// send the request to the server
// if success then close the modal
// if fail then alert the error
return;
}
e.preventDefault();
// make a post axios request to a server
Axios.post("http://localhost:8000/index.php", {
email: email,
password: password,
}).then((response) => {
if (response.data.message) {
console.log(response.data);
// console.log(loginStatus);
} else {
// alert the error
alert(response.data.error);
// history.push("/main");
}
});
setShow(false);
};
const login = () => {
Axios.post(url, {
email: email,
password: password,
}).then((response) => {
if (response.data.message) {
console.log(response.data);
// console.log(loginStatus);
} else {
// setLoginStatus(response.data[0].username);
history.push("/main");
}
});
};
//login validator
const loginValidate = () => {
if (email.length === 0) {
alert("Username cannot be empty.");
return;
}
if (password.length === 0) {
alert("Password cannot be empty.");
return;
}
login();
};
return (
<div>
<form>
<nav className="navbar navbar-expand-lg navbar-light fixed-top">
<div className="container">
<Link className="navbar-brand fs-1"> StuHuB </Link>{" "}
<div
className="collapse navbar-collapse"
id="navbarTogglerDemo02"
></div>{" "}
</div>{" "}
</nav>{" "}
<div className="auth-wrapper">
<div className="auth-inner">
<h3> Sign In </h3>{" "}
<div className="form-group">
<label> Login ID </label>{" "}
<input
type="text"
className="form-control"
placeholder="Enter Login ID"
onChange={(e) => {
setEmail(e.target.value);
}}
/>{" "}
</div>{" "}
<div className="form-group">
<label> Password </label>{" "}
<input
type="password"
className="form-control"
placeholder="Enter password"
onChange={(e) => {
setPassword(e.target.value);
}}
/>{" "}
</div>{" "}
<button
onClick={loginValidate}
type="button"
className="btn btn-primary btn-block"
>
Submit{" "}
</button>{" "}
</div>{" "}
<button className="btn btn-primary btn-block" onClick={handleShow}>
Register
</button>
</div>{" "}
</form>
{/* Not working? */}
{/* Modal */}
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Email address</Form.Label>
<Form.Control
type="email"
placeholder="name#example.com"
autoFocus
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
{/* form password */}
<Form.Item
name="password"
label="Password"
rules={[
{
required: true,
message: "Please enter password!",
},
]}
>
<input
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Item>
</Form>
</Modal.Body>
<Modal.Footer>
<button variant="secondary" onClick={handleClose}>
Close
</button>
<button variant="primary" onClick={handleSubmit}>
Submit
</button>
</Modal.Footer>
</Modal>
{/* make a hidden modal */}
</div>
);
}
export default LoginComponent;
I am stuck on getting my Modal to appear when the Edit button is clicked. I can see that the modalShow is being set to true when I click the button since I console.log the value. Any idea on how to get modal to appear when user clicks the edit button?
product-screen.jsx
import { useState } from 'react';
import { Button, Card, Form, Modal } from 'react-bootstrap';
import 'reactjs-popup/dist/index.css';
import ModalScreen from './modal-screen';
const ProductCardScreen = ({ product }) => {
const { productName, productPrice, productInStock, productDescription } =
product;
const [modalShow, setModalShow] = useState(false);
const [updateProduct, setProductUpdate] = useState({
productDescription,
productInStock,
productName,
productPrice,
});
const handleChange = (event) => {
console.log('event', event.target.value);
setProductUpdate({
...updateProduct,
[event.target.name]: event.target.value,
});
};
// todo
const saveUpdatedProduct = (product) => {
// save logic here to db
};
const handleDelete = () => {
alert('Are you sure you want to delete?');
};
const handleModalChange = () => {
console.log('called');
setModalShow(true);
};
return (
<div>
<Card>
<Card.Body>
<Card.Title>Product Name: {product.productName}</Card.Title>
<Card.Text>
Product Description: {product.productDescription}
</Card.Text>
<Card.Text>Product Quantity: {product.productInStock}</Card.Text>
<Card.Text>Price: ${product.productPrice}</Card.Text>
<div
style={{
float: 'right',
}}
>
<Button
style={{ margin: '10px' }}
onClick={() => handleModalChange()}
className='btn btn-primary'
variant='primary'
>
Edit
</Button>
<Button
onClick={() => handleDelete()}
className='btn btn-danger'
variant='primary'
>
Delete
</Button>
</div>
</Card.Body>
</Card>
<ModalScreen product={product} handleChange={handleChange} />
</div>
);
};
export default ProductCardScreen;
modal-screen.jsx
import { Button, Form, Modal } from 'react-bootstrap';
const ModalScreen = ({ product, handleChange }) => {
const { productName, productPrice, productInStock, productDescription } =
product;
return (
<Modal
animation={false}
size='lg'
aria-labelledby='contained-modal-title-vcenter'
centered
>
<Modal.Header>
<Modal.Title id='contained-modal-title-vcenter'>
Product Information
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className='mb-3'>
<Form.Label>Name</Form.Label>
<Form.Control
onChange={handleChange}
value={productName}
type='text'
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Description</Form.Label>
<Form.Control
onChange={handleChange}
value={productDescription}
type='text'
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Amount In Stock</Form.Label>
<Form.Control
onChange={handleChange}
value={productInStock}
type='text'
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Price</Form.Label>
<Form.Control
onChange={handleChange}
value={`$${productPrice}`}
type='text'
/>
</Form.Group>
<Button variant='primary' type='button'>
Save
</Button>
<Button variant='danger' type='button'>
Cancel
</Button>
</Form>
</Modal.Body>
<Modal.Footer></Modal.Footer>
</Modal>
);
};
export default ModalScreen;
I am a beginner in reactJS and would like to ask a simple question. I am following some tutorials and I really don't know why this wont work on mine. I know my question is very simple since I am a beginner. Please have a patience with my question.
I have this code from a function component form:
function Login() {
const emailInputRef = useRef();
const passwordInputRef = useRef();
function submitHandler(e) {
e.preventDefault();
const emailInput = emailInputRef.current.value;
const passwordInput = passwordInputRef.current.value;
console.log(emailInput);
}
return (
<>
<Col lg="5" md="7">
<Card className="bg-secondary shadow border-0">
<CardBody className="px-lg-5 py-lg-5">
<div className="text-center text-muted mb-4">
<small>Sign in</small>
</div>
<Form role="form" onSubmit={submitHandler}>
<FormGroup className="mb-3">
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-email-83" />
</InputGroupText>
</InputGroupAddon>
<Input
placeholder="Email"
type="email"
autoComplete="new-email"
ref={emailInputRef}
/>
</InputGroup>
</FormGroup>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-lock-circle-open" />
</InputGroupText>
</InputGroupAddon>
<Input
placeholder="Password"
type="password"
autoComplete="new-password"
ref={passwordInputRef}
/>
</InputGroup>
</FormGroup>
<div className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id=" customCheckLogin"
type="checkbox"
/>
<label
className="custom-control-label"
htmlFor=" customCheckLogin"
>
<span className="text-muted">Remember me</span>
</label>
</div>
<div className="text-center">
<Button className="my-4" color="primary" type="submit">
Sign in
</Button>
</div>
</Form>
</CardBody>
</Card>
<Row className="mt-3">
<Col xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Forgot password?</small>
</a>
</Col>
<Col className="text-right" xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Create new account</small>
</a>
</Col>
</Row>
</Col>
</>
);
}
I want to bind my input type from my submitHandler and log the values of the input types. I followed a tutorial using refs and I don't know if I made something wrong about it since I was a beginner. When I logged the values it gives me undefined values.
To get the current input reference use innerRef property instead of ref:
<Input
placeholder="Email"
type="email"
autoComplete="new-email"
innerRef={emailInputRef}
/>
const emailInput = emailInputRef.current.value;
ref will only get you a reference to the Input component, innerRef will get you a reference to the DOM input.
You have a few options for that. You can use the useRef React hook or the state stored using the useState React Hook. In both cases you need to import the related hook first. You could use both if you want. You could also use a custom hook with the useReducer React hook.
In your case it would be something like these:
Option # 1: Using useRef
import {useRef} from "react";
function Login() {
const emailInputRef = useRef();
const passwordInputRef = useRef();
function submitFormHandler(event) {
event.preventDefault();
const email = emailInputRef.current.value;
const password = passwordInputRef.current.value;
console.log(`email = ${email} & password = ${password}`);
}
return (
<>
<Col lg="5" md="7">
<Card className="bg-secondary shadow border-0">
<CardBody className="px-lg-5 py-lg-5">
<div className="text-center text-muted mb-4">
<small>Sign in</small>
</div>
<Form role="form" onSubmit={submitFormHandler}>
<FormGroup className="mb-3">
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-email-83"/>
</InputGroupText>
</InputGroupAddon>
<Input
placeholder="Email"
type="email"
id='email'
name='email'
required
autoComplete="new-email"
ref={emailInputRef}
/>
</InputGroup>
</FormGroup>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-lock-circle-open"/>
</InputGroupText>
</InputGroupAddon>
<input
placeholder="Password"
type="password"
id="password"
name="password"
required
autoComplete="new-password"
ref={passwordInputRef}
/>
</InputGroup>
</FormGroup>
<div className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id=" customCheckLogin"
type="checkbox"
/>
<label
className="custom-control-label"
htmlFor=" customCheckLogin"
>
<span className="text-muted">Remember me</span>
</label>
</div>
<div className="text-center">
<Button className="my-4" color="primary" type="submit">
Sign in
</Button>
</div>
</Form>
</CardBody>
</Card>
<Row className="mt-3">
<Col xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Forgot password?</small>
</a>
</Col>
<Col className="text-right" xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Create new account</small>
</a>
</Col>
</Row>
</Col>
</>
);
};
export default Login;
Option # 2: Using useState
import {useState} from "react";
function Login() {
const [emailState, setEmailState] = useState('');
const [passwordState, setPasswordState] = useState('');
function submitFormHandler(event) {
event.preventDefault();
console.log(`email = ${emailState} & password = ${passwordState}`);
}
const onEmailChangeHandler = (event) => {
setEmailState(event.current.value);
};
const onPasswordChangeHandler = (event) => {
setPasswordState(event.current.value);
};
return (
<>
<Col lg="5" md="7">
<Card className="bg-secondary shadow border-0">
<CardBody className="px-lg-5 py-lg-5">
<div className="text-center text-muted mb-4">
<small>Sign in</small>
</div>
<Form role="form" onSubmit={submitFormHandler}>
<FormGroup className="mb-3">
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-email-83"/>
</InputGroupText>
</InputGroupAddon>
<input
placeholder="Email"
type="email"
id='email'
name='email'
required
autoComplete="new-email"
value={emailState}
onChange={onEmailChangeHandler}
/>
</InputGroup>
</FormGroup>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-lock-circle-open"/>
</InputGroupText>
</InputGroupAddon>
<input
placeholder="Password"
type="password"
id="password"
name="password"
required
autoComplete="new-password"
value={passwordState}
onChange={onPasswordChangeHandler}
/>
</InputGroup>
</FormGroup>
<div className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id=" customCheckLogin"
type="checkbox"
/>
<label
className="custom-control-label"
htmlFor=" customCheckLogin"
>
<span className="text-muted">Remember me</span>
</label>
</div>
<div className="text-center">
<Button className="my-4" color="primary" type="submit">
Sign in
</Button>
</div>
</Form>
</CardBody>
</Card>
<Row className="mt-3">
<Col xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Forgot password?</small>
</a>
</Col>
<Col className="text-right" xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Create new account</small>
</a>
</Col>
</Row>
</Col>
</>
);
};
export default Login;
Option # 3: Using both React hooks (useRef & useState).
You could use the useRef React hook for focusing purposes only and also use a local state value, using the useState React hook, for the usual purposes of login in.
Option # 4: Using custom hooks.
You can use all the mentioned before, but using primordially a custom hook, which uses the useReducer React hook under the trunk, and then useRef for focusing and useState for other purposes:
Coder example:
Custom hook:
import {useReducer} from "react";
const initialInputState = {
valueState: '',
valueIsTouchedState: false,
};
const inputStateReducer = (state, action) => {
if (action.type === 'SET_VALUE_IS_TOUCHED_STATE') {
return {
valueState: state.valueState,
valueIsTouchedState: action.payload.valueIsTouchedState
};
}
if (action.type === 'SET_VALUE_STATE') {
return {
valueState: action.payload.valueState,
valueIsTouchedState: state.valueIsTouchedState
};
}
return initialInputState;
};
const useInputReducer = (valueValidator) => {
const [inputState, dispatchFunction] = useReducer(inputStateReducer, initialInputState);
const valueIsValidState = valueValidator(inputState.valueState);
const valueInputIsInvalid = (!valueIsValidState && inputState.valueIsTouchedState);
const valueInputChangeHandler = (event) => {
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: true}});
dispatchFunction({type: 'SET_VALUE_STATE', payload: {valueState: event.target.value}});
};
const valueInputBlurHandler = (event) => {
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: true}});
// setValueState(event.target.value);
};
const setValueIsTouchedState = (value) => {
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: value}});
};
const resetValueInput = () => {
dispatchFunction({type: 'SET_VALUE_STATE', payload: {valueState: ''}});
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: false}});
};
return {
valueState: inputState.valueState,
setValueIsTouchedState,
valueIsValidState,
valueInputIsInvalid,
valueInputChangeHandler,
valueInputBlurHandler,
resetValueInput,
};
};
export default useInputReducer;
Form:
import {Fragment, useRef, useState} from 'react';
import {Prompt} from "react-router-dom";
import useInputReducer from "../../hooks/use-input-reducer";
import * as validators from "../../tools/validators";
import styles from './AuthForm.module.css';
const AuthForm = () => {
const [isLogin, setIsLogin] = useState(true);
const [startedToWork, setStartedToWork] = useState(false);
const {
valueState: emailState,
setValueIsTouchedState: setEmailIsTouchedState,
valueIsValidState: emailIsValidState, valueInputIsInvalid: emailInputIsInvalid,
valueInputChangeHandler: emailInputChangeHandler,
// valueInputBlurHandler: emailInputBlurHandler,
resetValueInput: resetEmailInput,
} = useInputReducer(validators.emailValidator);
const {
valueState: passwordState,
setValueIsTouchedState: setPasswordIsTouchedState,
valueIsValidState: passwordIsValidState, valueInputIsInvalid: passwordInputIsInvalid,
valueInputChangeHandler: passwordInputChangeHandler,
// valueInputBlurHandler: passwordInputBlurHandler,
resetValueInput: resetPasswordInput,
} = useInputReducer(validators.nameValidator);
const formIsValid = (emailIsValidState && passwordIsValidState);
const emailInputRef = useRef();
const passwordInputRef = useRef();
const submitFormHandler = (event) => {
event.preventDefault();
setEmailIsTouchedState(true);
setPasswordIsTouchedState(true);
emailInputRef.current.focus();
if (!formIsValid) {
setProperFocus();
return;
}
console.log('Submitted!. I did something!!');
const email = emailInputRef.current.value;
const password = passwordInputRef.current.value;
resetEmailInput();
resetPasswordInput();
};
const setProperFocus = () => {
if (emailInputIsInvalid) {
emailInputRef.current.focus();
} else if (passwordInputIsInvalid) {
passwordInputRef.current.focus();
}
}
const switchAuthModeHandler = () => {
setIsLogin((prevState) => !prevState);
};
const onStartedToWorkHandler = () => {
setStartedToWork(true);
};
const onFinishEnteringHandler = () => {
setStartedToWork(false);
};
const emailValidityClasses = `${styles.control}${emailInputIsInvalid ? ` ${styles.invalid}` : ''}`;
const passwordValidityClasses = `${styles.control}${passwordInputIsInvalid ? ` ${styles.invalid}` : ''}`;
return (
<Fragment>
<Prompt
when={startedToWork}
message={location =>
`Are you sure you want to go to "${location.pathname}"? \n All your entered data will be lost.`
}
/>
<section className={styles.auth}>
<h1>{isLogin ? 'Login' : 'Sign Up'}</h1>
<form
onSubmit={submitFormHandler}
onChange={onStartedToWorkHandler}
>
<div className={emailValidityClasses}>
<label htmlFor='email'>Your Email</label>
<input
type='email'
id='email'
name='email'
required
autoFocus={true}
ref={emailInputRef}
value={emailState}
onChange={emailInputChangeHandler}
// onBlur={emailInputBlurHandler}
/>
{emailInputIsInvalid ? <p className={styles['error-text']}>The Email must be valid.</p> : <p> </p>}
</div>
<div className={passwordValidityClasses}>
<label htmlFor='password'>Your Password</label>
<input
type='password'
id='password'
required
autoComplete="on"
ref={passwordInputRef}
value={passwordState}
onChange={passwordInputChangeHandler}
// onBlur={passwordInputBlurHandler}
/>
{passwordInputIsInvalid ? <p className={styles['error-text']}>The Password must not be empty.</p> : <p> </p>}
</div>
<div className={styles.actions}>
<button onClick={onFinishEnteringHandler}>{isLogin ? 'Login' : 'Create Account'}</button>
<button
type='button'
className={styles.toggle}
onClick={switchAuthModeHandler}
>
{isLogin ? 'Create new account' : 'Login with existing account'}
</button>
</div>
</form>
</section>
</Fragment>
);
};
export default AuthForm;
I never do react js but i code react native.
I think you can store the input in the state using onChangeText like this
using hooks
const index = ()=>{
const {state,setState} = useState('');
return <TextInput onChangeText={(txt)=>{
setState(txt);
console.log(state); }} />
}
I have a multistep form for signing up. What is the easiest and the best approach for form validation? What should I use? Is it alright to use Formik for that? Could you suggest to me what to do?
Here is one of the forms:
return(
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control type="text" value={formValues.firstName} onChange={handleChange('firstName')} />
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control type="text" value={formValues.lastName} onChange={handleChange('lastName')} />
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control type="email" value={formValues.email} onChange={handleChange('email')} />
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" value={formValues.password} onChange={handleChange('password')} />
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control type="tel" value={formValues.phone} onChange={handleChange('phone')} />
</Form.Group>
<Button variant="light" type="submit" size="lg" onClick={redirectToHome}>Cancel</Button>
<Button variant="primary" type="submit" size="lg" onClick={saveAndContinue}>Next</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
You can use Formik and a Yup for validation schema:
https://formik.org/docs/api/formik#validationschema-schema----schema
npm links:
https://www.npmjs.com/package/formik
https://www.npmjs.com/package/yup
It will look like this:
export default function MyForm() {
const initValues = {
firstName: "",
lastName: "",
email: "",
password: "",
phone: ""
};
const schema = Yup.object().shape({
firstName: Yup.string(),
lastName: Yup.string(),
email: Yup.string().email("Email format is invalid."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/[a-zA-Z]/, "Password can only contain Latin letters."),
phone: Yup.string().phone(" ")
});
return (
<Formik validationSchema={schema} initialValues={initValues}>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
setFieldTouched
} = props;
return (
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control
name="firstName"
type="text"
value={values.firstName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control
name="lastName"
type="text"
value={values.lastName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control
name="email"
type="email"
value={values.email}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
type="password"
value={values.password}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control
name="phone"
type="tel"
value={values.phone}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Button
variant="light"
type="submit"
size="lg"
onClick={redirectToHome}
>
Cancel
</Button>
<Button
variant="primary"
type="submit"
size="lg"
onClick={saveAndContinue}
>
Next
</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
}}
</Formik>
);
}
But you should change also your Form.Control component:
Add
import { ErrorMessage, getIn } from "formik";
const error = getIn(props.errors, props.name);
and add to your Input these attributes
onChange={(e) => {setFieldTouched("firstName");handleChange(e);}
error={error}
and Error to show
<Error component="div" name={props.name} />
I would suggest adding your own validators and bundling inputs that are of the same type. For example First Name and Last Name are both strings so you can have a validator for string types. You could do it like this
//validator.js
const stringValidator = (value) => {
if(isEmpty(value)){
return { isValid: false, errors: ["Field cannot be blank"] }
}
return { isValid: true, errors: [] }
}
const config = {
firstName: stringValidator,
lastName: stringValidator,
email: emailValidator,
cellphone: cellphoneValidator,
password: passwordValidator
}
const getValidator = (questionId) => config[questionId];
export default (questionId, value) => {
const validate = getValidator(questionId);
return validate(value)
}
...
// Form.js class
answerQuestion(questionId){
return (e) => {
const answer = e.target.value;
const validation = validate(questionId, answer)
const { errors } = validation;
let { fields } = this.state;
fields[questionId] = { errors, value, label: field.label }
this.setState({ fields })
}
}
...
<Form>
{this.state.fields.map( field => {
return(
<Form.Group>
<Form.Label>{field.label}</Form.Label>
<Form.Control type="text" value={field.value} onChange={this.answerQuestion(field.questionId)} />
{field.errors.map(err => <span>{err}</span>}
</Form.Group>
)
}
</Form>
I'm trying to get the currently logged in users email and use it in my fetch() call. I can currently get the email from getfirstapi() and use it in my form but I'm having trouble passing it into my getSecondApi() where the fetch() is ?
Iv tried creating a getEmail function to return it, but with no luck.
Code
import * as React from "react";
import axios from "axios";
import { Form, Card, Grid } from "tabler-react";
import { Button, Modal } from "semantic-ui-react";
import Auth from '#aws-amplify/auth';
import '../../index.css';
import { arrayOf } from "prop-types";
//const config = require('../config.json');
class GeneralInformation extends React.Component {
constructor(props) {
super(props);
this.state = {
// States from API
firstname: "",
middlename: "",
surname: "",
city: "",
postcode: "",
state: "",
email: "",
about: "",
// States for editable form
formfirstname: "",
formmiddlename: "",
formsurname: "",
formcity: "",
formpostcode: "",
formstate: "",
formabout: "",
// Modal State
open: false,
};
}
getUseremail() {
return Auth.currentAuthenticatedUser().then(user => user.attributes.email)
}
email = Auth.currentAuthenticatedUser().then(user => user.attributes.email)
getFirstApi() {
return Auth.currentAuthenticatedUser().then((user) => {
this.setState({email: user.attributes.email, formemail: user.attributes.email})
});
}
getSecondApi(email) {
fetch(`https://ezha2ns0bl.execute-api.ap-southeast-2.amazonaws.com/prod/userdata?foo=${encodeURIComponent(email)}`)
.then(res => res.json())
.then(
console.log("THIS IS RESULT2 " + email),
(result) => {
this.setState({
firstname: result.Item.userFirstName,
middlename: result.Item.userMiddleName,
surname: result.Item.userLastName,
city: result.Item.userCity,
postcode: result.Item.userPostcode,
state: result.Item.userState,
about: result.Item.userAbout,
formfirstname: result.Item.userFirstName,
formmiddlename: result.Item.userMiddleName,
formsurname: result.Item.userLastName,
formcity: result.Item.userCity,
formpostcode: result.postcode,
formstate: result.Item.userState,
formabout: result.Item.userAbout,
});
console.log("THIS IS RESULT1 " + result)} ,
)
}
componentDidMount() {
this.getFirstApi();
this.getSecondApi();
}
handleChange = (input) => (event) => {
this.setState({ [input]: event.target.value });
};
handleSubmit = async (event) => {
event.preventDefault();
this.setState((prevState) => ({
// If submitting new values, update the state to represent the new data
firstname: prevState.formfirstname,
middlename: prevState.formmiddlename,
surname: prevState.formsurname,
city: prevState.formcity,
postcode: prevState.formpostcode,
email: prevState.formemail,
userState: prevState.formstate,
about: prevState.formabout,
open: false,
}))
try {
const params = {
"userFirstName": this.state.formfirstname,
"userMiddleName": this.state.formmiddlename,
"userLastName": this.state.formsurname,
"userCity": this.state.formcity,
"userPostCode": this.state.formpostcode,
"userEmail": this.state.formemail,
"userState": this.state.formstate,
"userAbout": this.state.formabout,
"userType": "jobseeker"
};
await axios.post('https://qrg3idkox4.execute-api.ap-southeast-2.amazonaws.com/prod/{userEmail}/', params);
}catch (err) {
console.log(`An error has occurred: ${err}`);
}
};
cancelForm = () => {
// If cancelling, reset any fields that have been changed to the original values so that when the modal is re-opened, the old values are shown
this.setState((prevState) => ({
formfirstname: prevState.firstname,
formmiddlename: prevState.middlename,
formsurname: prevState.surname,
formcity: prevState.city,
formpostcode: prevState.postcode,
formstate: prevState.state,
formabout: prevState.about,
open: false,
}));
};
openModal = () => {
this.setState({ open: true });
};
render() {
const {
firstname,
middlename,
surname,
city,
postcode,
state,
email,
about,
formfirstname,
formmiddlename,
formsurname,
formcity,
formpostcode,
formstate,
formabout,
open,
} = this.state;
return (
<div className="card" name="generalInfo">
<Card.Body>
<Grid>
<Grid.Row>
<Grid.Col md={7}>
<Card.Title>General Information</Card.Title>
</Grid.Col>
<Grid.Col md={5}>
{/* MODAL BUTTON */}
<Button
floated="right"
basic
icon="pencil"
type="button"
compact
onClick={this.openModal}
/>
</Grid.Col>
</Grid.Row>
<Grid.Row>
{/* ROW 1 */}
<Grid.Col md={4}>
<Form.Group label="First Name">
<Form.Input name="firstname" readOnly value={firstname} />
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Middle Name">
<Form.Input name="middlename" readOnly value={middlename} />
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Surname">
<Form.Input name="surname" readOnly value={surname} />
</Form.Group>
</Grid.Col>
{/* ROW 2 */}
<Grid.Col md={3}>
<Form.Group label="City">
<Form.Input name="city" readOnly value={city} />
</Form.Group>
</Grid.Col>
<Grid.Col md={2}>
<Form.Group label="Post Code">
<Form.Input name="postcode" readOnly value={postcode} />
</Form.Group>
</Grid.Col>
<Grid.Col md={3}>
<Form.Group label="State">
<Form.Input name="state" readOnly value={state} />
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Email">
<Form.Input name="email" readOnly value={email} />
</Form.Group>
</Grid.Col>
{/* ROW 3 */}
<Grid.Col md={12}>
<Form.Group className="mb=0" label="About Me">
<Form.Textarea
name="aboutme"
rows={3}
disabled
readOnly
value={about}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
</Grid>
</Card.Body>
{/* MODAL CONTENT */}
<Modal
style={{ position: "relative" }}
closeOnDimmerClick={false}
open={open}
>
<Modal.Header>Edit Info</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Grid.Row>
<Grid.Col md={4}>
<Form.Group label="First Name">
<Form.Input
name="firstname"
value={formfirstname}
onChange={this.handleChange("formfirstname")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Middle Name">
<Form.Input
name="middlename"
value={formmiddlename}
onChange={this.handleChange("formmiddlename")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Surname">
<Form.Input
name="surname"
value={formsurname}
onChange={this.handleChange("formsurname")}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
{/* ROW 2 */}
<Grid.Row>
<Grid.Col md={3}>
<Form.Group label="City">
<Form.Input
name="city"
value={formcity}
onChange={this.handleChange("formcity")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={2}>
<Form.Group label="Post Code">
<Form.Input
name="postcode"
value={formpostcode}
onChange={this.handleChange("formpostcode")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={3}>
<Form.Group label="State">
<Form.Input
name="state"
value={formstate}
onChange={this.handleChange("formstate")}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
{/* ROW 3 */}
<Grid.Row>
<Grid.Col md={12}>
<Form.Group className="mb=0" label="About Me">
<Form.Textarea
name="aboutme"
rows={3}
value={formabout}
onChange={this.handleChange("formabout")}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
{/* ROW 4 - SUBMIT */}
<Grid.Row>
<Grid.Col md={12}>
<Button
floated="left"
basic
type="button"
color="red"
onClick={this.cancelForm}
>
{" "}
Cancel{" "}
</Button>
<Button floated="right" basic type="submit" color="green">
{" "}
Accept Changes{" "}
</Button>
</Grid.Col>
</Grid.Row>
</Form>
</Modal.Content>
</Modal>
</div>
);
}
}
export default GeneralInformation;
Form
EDIT
> getEmailApi() { return
> Auth.currentAuthenticatedUser().then((user) => {
> const { attributes = {} } = user;
> console.log(attributes['email']);
> })}
iv created this function to get the email
but don't know how to pass it into the getSecondApi
My Solution
getEmailApi() {
return Auth.currentAuthenticatedUser().then((user) => {
const { attributes = {} } = user;
console.log(attributes['email']);
let email = attributes['email']
return email
})}
getFirstApi() {
return Auth.currentAuthenticatedUser().then((user) => {
this.setState({email: user.attributes.email, formemail: user.attributes.email})
});
}
getSecondApi(email) {
fetch(`https://ezha2ns0bl.execute-api.ap-southeast-2.amazonaws.com/prod/userdata?userEmail=${encodeURIComponent(email)}`)
.then(res => res.json())
.then(
console.log("THIS IS RESULT2 " + email),
(result) => {
this.setState({
firstname: result.Item.userFirstName,
middlename: result.Item.userMiddleName,
surname: result.Item.userLastName,
city: result.Item.userCity,
postcode: result.Item.userPostcode,
state: result.Item.userState,
about: result.Item.userAbout,
formfirstname: result.Item.userFirstName,
formmiddlename: result.Item.userMiddleName,
formsurname: result.Item.userLastName,
formcity: result.Item.userCity,
formpostcode: result.postcode,
formstate: result.Item.userState,
formabout: result.Item.userAbout,
});
console.log("THIS IS RESULT1 " + result)} ,
)
}
BeforDidMount() {
this.getEmailApi().then(email => this.getSecondApi(email)); }
componentDidMount() {
this.BeforDidMount();
this.getFirstApi();
}