I'm new to react thus the question.
This is my CustomModal,
import React from 'react';
import {useState} from "react";
import {Button, Modal} from "react-bootstrap";
const CustomModal = (props) =>{
const [show, setShow] = useState(true);
const handleClose = () => setShow(false);
return (
<div>
<Modal show={show} animation={false}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>{props.message}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
)
};
export default CustomModal;
I want to render it inside a class component when user submits a registration form.
class Register extends React.Component {
state = {
email : '',
username: '',
password: '',
second_password: ''
};
handleSubmit = async (event) =>{
event.preventDefault();
const emailValidated = await this.validateEmail(this.state.email);
const usernameValidated = this.validateUsername(this.state.username);
const passwordValidated = this.validatePassword(this.state.password, this.state.second_password);
let registrationComplete = false;
if(emailValidated === true && usernameValidated === true && passwordValidated === true){
registrationComplete = await this.register(this.state.email, this.state.username, this.state.password);
console.log(registrationComplete);
this.showModal("Hello World!"); //This is the function begin called to show the modal.
}
};
validateUsername = (username) =>{
return true;
};
validatePassword = (password, second) =>{
return true;
};
validateEmail = async (email) =>{
return true;
};
//This is the function that should display the modal
showModal = (message) =>{
return (<CustomModal message={message}/>);
};
register = async (email, username, password) =>{
return true;
};
render() {
return (
<div className="register">
<h1>Register</h1>
<Form onSubmit={this.handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control type="email"
placeholder="Enter email"
value={this.state.email}
onChange={event => this.setState({email: event.target.value})}/>
<Form.Text className="text-muted">
Please make sure you've access to this mail. You'll receive an activation code here.
</Form.Text>
</Form.Group>
<Form.Group controlId="formPlainText">
<Form.Label className="form-label">Username</Form.Label>
<Form.Control type="text"
placeholder="Username"
value={this.state.username}
onChange={event => this.setState({username: event.target.value})}/>
<Form.Text className="text-muted">
Please make it atleast 8 characters long.
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password"
placeholder="Password"
value={this.state.password}
onChange={event => this.setState({password: event.target.value})}/>
<Form.Text className="text-muted">
Please make sure it's atleast 8 characters long and uses a mix of letters and numbers.
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Retype Password</Form.Label>
<Form.Control type="password"
placeholder="Password"
value={this.state.second_password}
onChange={event => this.setState({second_password: event.target.value})}/>
</Form.Group>
<Button variant="primary" type="submit">
Register
</Button>
</Form>
</div>
);
}
}
export default Register;
All the values are correct and the console log shows true but the Modal doesn't display. Can someone help me with this?
There are some issues on your code
The customModal has to be part of the render of the Register component
There should be a state to track the status of the modal from the Register component
Also the register has to be notified when the Modal closes
I have modified your code and working. You can find it live here
I prefer attack this problem creating portals here documentation.
you might to have issues with z-index in the future.
Basically, you create an element outside the DOM hierarchy.
Now, your issue is that, you must render modal inside render method and control it, with boolean state "showModal".
I prepare for you an example:
example in my GitHub account
git clone ...
npm install
npm run start
preview:
Related
Problem
I'm working on project using reactjs and graphql with apollo client. I'm trying to get user by id and it works, the value shows in the form modal.
But, I can't delete or add the text or content inside form input.
I'm using onChange handler but it doesn't work. here's the code
import React, { useEffect, useState } from "react";
import { useMutation, useQuery } from "#apollo/client";
import { Button, Modal, Form } from "react-bootstrap";
import { GET_USER_BY_ID } from "../../../gql/query";
const ModalEdit = (props) => {
// state for check input component
const [isChecked, setIsChecked] = useState('ACTIVE');
const [value, setValue] = useState({
full_name: "",
email: "",
phone: "",
address: "",
password: "",
group_id: "",
});
useEffect(() => {
if (props.show) {
document.body.classList.add("modal-open");
}
return () => {
if (document.body.classList.contains("modal-open")) {
document.body.classList.remove("modal-open");
}
};
}, [props.show]);
const { data, loading, error } = useQuery(GET_USER_BY_ID, {
variables: { username: props.username },
});
const dataUser = data?.getUserByID;
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
const onChange = (event) => {
setValue({
...value,
[event.target.name]: event.target.value
})
}
return (
<Modal show={props.show}>
<Modal.Header>
<Modal.Title> <span>FORMULIR AKUN PENGGUNA</span> </Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className="mb-3">
<Form.Label>Role Akun</Form.Label>
<Form.Select aria-label="pilih user role">
<option>{dataUser.group_id}</option>
<option>Admin RJ</option>
</Form.Select>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Nama Lengkap</Form.Label>
<Form.Control name="full_name" value={dataUser.full_name} onChange= {onChange} />
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Email</Form.Label>
<Form.Control type="email" name="email" value={dataUser.email} onChange={ onChange }/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Phone</Form.Label>
<Form.Control type="text" name="phone" value={dataUser.phone} onChange={ onChange } />
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Address</Form.Label>
<Form.Control type="text" name="address" value={dataUser.address} onChange={ onChange } />
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Password</Form.Label>
<Form.Control type="password" name="password" value={dataUser.password} onChange={ onChange } />
</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>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" type="submit" >Submit</Button>
<Button variant="secondary" onClick={props.onClose}>
Close
</Button>
</Modal.Footer>
</Modal>
);
};
export default ModalEdit;
Question
How can i'm edit the form input?
Any help will be apprieciated, thank you
The value prop of your inputs are causing this issue. Instead of binding the value directly to dataUser.address and such, you should set them to value.address and so on.
As for showing the user's data, you should map the values when creating the state.
Your state should be as follows:
const [value, setValue] = useState({
full_name: dataUser.full_name,
email: dataUser.email,
phone: dataUser.phone
address: dataUser.address,
password: dataUser.password,
group_id: dataUser.group_id,
});
And your input should be as follows:
<Form.Control name="full_name" value={value.full_name} onChange= {onChange} />
Trying to make the onChange event of the following componenet to fire but it does not. When I type in the seach box nothing is printed on the console. No change is fired
const Search = () => {
let history = useHistory()
const [search, setSearch] = useState('')
const handleSubmit = (e) => {
e.preventDefault()
if (search) {
console.log("typing..")
history.push(`/?search=${search}`)
} else {
history.push(history.push(history.location.pathname))
}
}
return (
<Form.Group controlId='searchbox' onSubmit={handleSubmit}>
<Form.Control
className="auth-input"
type='text'
placeholder="Search..."
onChange={(e) => setSearch(e.target.value)}
>
</Form.Control>
<Button type='submit'>Submit</Button>
</Form.Group>
)
}
export default Search
const Home =()=>{
return (<div><Search /><div>)
}
export default Home
I still can't identify where the error is. How do I get it working?
I am assuming Form.Control is a custom control or a third-party control, hence you cannot convert it to a controlled input. You could try creating a ref and get the value from the ref in the onChange event handler. If the custom control is not a third party control, then you could add the onChange handler inside the Form.Control component and pass a function reference to the control.
Edit:
Here is your solution : https://codesandbox.io/s/vigorous-mclean-4jusl?file=/src/App.js
In case you see an error in the sandbox, refresh the page. There seems to be some error with codesandbox.
Explanation:
Create a ref to access the input using useRef:
const controlref = useRef();
Pass the ref to the control:
<Form.Control
onChange={handleChange}
**ref={controlref}**
type="text"
placeholder="search..."
/>
Get the value in onChange using the ref:
const handleChange = (e) => {
console.log(controlref.current.value);
setSearch(controlref.current.value);
};
Firstly, html "input" is a self closing element.
Secondly, You have missed the "value" inside the Input component.
Your code:
<Form.Control
className="auth-input"
type='text'
placeholder="Search..."
onChange={(e) => setSearch(e.target.value)}
></Form.Control>
Solution:
<Form.Control
className="auth-input"
type='text'
placeholder="Search..."
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
Try using event onInput instead:
<Form.Control
className="auth-input"
type='text'
placeholder="Search..."
onInput={(e) => setSearch(e.target.value)}
/>
I can't believe I made this silly mistake. The issue is due to the fact that I omitted the tag and placed the submit button within the <Form.Group> which doesn't have the property to handle onSubmit event. So I changed this
return (
<Form.Group controlId='searchbox' onSubmit={handleSubmit}>
<Form.Control
className="auth-input"
type='text'
placeholder="Search..."
onChange={(e) => setSearch(e.target.value)}
>
</Form.Control>
<Button type='submit'>Submit</Button>
</Form.Group>
)
It was modified to this:
return (
<Form onSubmit={handleSubmit} >
<Form.Group controlId='searchbox' >
<Form.Control
className="auth-input"
type='text'
placeholder="Search..."
onChange={(e) => setSearch(e.target.value)}
>
</Form.Control>
<Button type='submit'>Submit</Button>
</Form.Group>
</Form>
)
I am unable to get the login credentials in the object, the code below works perfectly fine for other forms
SignIn.js
export default function SignIn(props) {
const [state, setState] = useState({
username:'',
password:''
});
const handleSubmit = (event) =>{
event.preventDefault()
const loginCredentials = state;
console.log(loginCredentials)
}
const handleChange = (evt, name)=>{
const { value } = evt.target;
setState({
...state,
[name]: value
});
props.loginHandler()
}
const classes = useStyles();
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="username"
name="name"
autoComplete="email"
autoFocus
onChange={(event)=>handleChange( event, "username")}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={(event)=>handleChange( event, "password")}
/>
<Button onClick={handleSubmit}
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
</form>
</div>
</Container>
);
}
Moreover can any one guide me how to handle file responses in react, I have an endpoint which has a csv file as a response how can I write the request so that the file starts downloading on the press of a button
If I understand your necessity, you can do this directly in the button onChange event, like this:
onChange={(event) => setName(event.target.value)}
onChange={(event) => setPassword(event.target.value)}
and you can create some state like this:
const [values, setValues] = useState({ name: '', password: '' });
...and apply these data in your submit function:
const handleSubmit = () =>{
setValues({ name, password });
console.log(values);
}
I am trying to add some validation to my React + Bootstrap project but I am not able to achieve this. According to Bootstrap Form validation documentation I have to provide a Form.Control.Feedback component to the Form Group component.
But is not showing the errors but check marks like everything is fine. I created this Code Sandbox yo demostrate what I need to achieve and what is showing me:
And just in case, this is the code if you don't need the sandbox to see the error:
import React, { useState } from "react";
import "./styles.css";
import { Container, Form, Button } from "react-bootstrap";
import validator from "validator";
import empty from "is-empty";
export default function App() {
const [validated, setValidated] = useState(false);
const [errors, setErrors] = useState({});
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const handleSubmit = async e => {
e.preventDefault();
const errors = {};
// validation
if (!validator.isMobilePhone(phone, ["es-UY"])) {
errors.phone = "Invalid phone number";
}
if (!validator.isEmail(email)) {
errors.email = "Invalid email address";
}
if (!empty(errors)) {
setErrors(errors);
}
setValidated(true);
};
return (
<Container>
<Form validated={validated} onSubmit={handleSubmit}>
<h1>Test form</h1>
<Form.Group controlId="phone">
<Form.Label>Phone number</Form.Label>
<Form.Control
type="tel"
value={phone}
onChange={e => setPhone(e.target.value)}
/>
<Form.Control.Feedback type="invalid">Error</Form.Control.Feedback>
{errors.phone && (
<Form.Control.Feedback type="invalid">
{errors.phone}
</Form.Control.Feedback>
)}
</Form.Group>
<Form.Group controlId="email">
<Form.Label>Email address</Form.Label>
<Form.Control
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
feedback="Error"
/>
{errors.email && (
<Form.Control.Feedback type="invalid">
{errors.email}
</Form.Control.Feedback>
)}
</Form.Group>
<Form.Group controld="submit">
<Button type="submit" variant="primary">
Submit
</Button>
<Button
type="reset"
variant="info"
style={{ marginLeft: 10 }}
onClick={() => {
setErrors({});
setValidated(false);
}}
>
Reset
</Button>
</Form.Group>
</Form>
</Container>
);
}
So, what am I doing wrong?
Make sure you've set setValidated as false for the errors
const handleSubmit = async e => {
e.preventDefault();
const allErrors = {}; // <--- changed this as well
// validation
if (!validator.isMobilePhone(phone, ["es-UY"])) {
allErrors.phone = "Invalid phone number";
}
if (!validator.isEmail(email)) {
allErrors.email = "Invalid email address";
}
if (!empty(allErrors)) {
setErrors(allErrors);
setValidated(false);
}
else {
setValidated(true);
}
};
In your field add isInvalid as props:
<Form.Control
type="tel"
value={phone}
isInvalid={!!errors.phone} <------
onChange={e => setPhone(e.target.value)}
/>
<Form.Control
type="text"
value={email}
isInvalid={!!errors.email} <-----
onChange={e => setEmail(e.target.value)}
feedback="Error"
/>
I am trying to initiate conditional rendering based on a form submit that goes to my node router but I am not getting the result. The buttons do not hide the content "ReminderModel" & "Reminder Table" and nothing happens when I submit the login form. I know this isnt the best way to do it but fi someone could point me in the right direction I would appreciate it. I would prefer not to use another plugin for react and would prefer to do it on my own to avoid update conflicts, etc.
import React, { Component } from 'react';
import { Container, Button, Form, Col } from 'react-bootstrap';
import axios from 'axios';
import './App.css';
import ReminderTable from './components/reminderList';
import ReminderModal from './components/modal';
const redColor = {
color: '#e52d2d'
}
class App extends Component {
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.LoginButton = this.LoginButton.bind(this);
this.UponLoggedIn = this.UponLoggedIn.bind(this);
this.NotLoggedIn = this.NotLoggedIn.bind(this);
this.state = {
user:[],
isLoggedIn:false,
username:'',
password:''
};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
LoginButton = props => {
return(
<>
<div id="status"></div>
<Form
className="loginForm"
>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>Username</Form.Label>
<Form.Control
type="text"
name="username"
id="username"
value={this.state.value}
onChange={this.handleChange}
>
</Form.Control>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
id="password"
name="password"
value={this.state.value}
onChange={this.handleChange}
/>
</Form.Group>
</Form.Row>
<Button className="btn btn-sm btn-light" onClick={this.onSubmit}>
<i style={redColor} className="fas fa-sign-in-alt"></i> Login
</Button>
</Form>
</>
);
}
LogoutButton = props => {
return(
<>
<Button className="btn btn-sm btn-light float-right" onClick={this.NotLoggedIn}>
<i style={redColor} className="fas fa-sign-out-alt"></i> Logout
</Button>
</>
)
}
NotLoggedIn = props => {
return(
<>
<h4 className="text-muted"> Please Log in</h4>
{this.LoginButton()}
</>
)
}
UponLoggedIn = props => {
return(
<>
<ReminderModal />
<p className="text-muted text-center">If new reminder does not show up immediently please refresh page</p>
<ReminderTable />
</>
)
}
ViewScreen = props => {
const isLoggedIn = props.isLoggedIn;
if(isLoggedIn){
return this.UponLoggedIn();
}else {
return this.NotLoggedIn();
}
}
onSubmit = (e) => {
e.preventDefault();
axios.get('api/user')
.then(res => {
const user = res.data[0].username;
const password = res.data[0].password;
const username = this.state.username;
const passwordEntered = this.state.password;
if(user === username && passwordEntered === password){
if(username === '' && passwordEntered === ''){
document.getElementById('status').innerHTML = '<p>Please Enter A Valid Username and Password</p>';
this.NotLoggedIn();
}else{
document.getElementById('status').innerHTML = '<p>Please Enter A Valid Username and Password</p>';
this.NotLoggedIn();
}
this.UponLoggedIn();
}else {
this.NotLoggedIn();
}
})
.catch(error => {
console.log(error);
});
}
render(){
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = this.LoginButton();
} else {
button = this.LogoutButton();
}
return (
<div className="App container">
<h4
className="display-4 mt-4 mb-4 text-center"
>
<i style={redColor}
className="fas fa-asterisk">
</i> Expiration Reminder
</h4>
<Container isLoggedIn={isLoggedIn}>{button}</Container>
</div>
);
}
}
export default App;
You are calling this.NotLoggedIn in onClick handler, which is wrong, because NotLoggedIn returns React component. In the onCLick handler, you need to change state. So you should call this.handleLoginClick and this.handleLogoutClick instead.
Also there are couple of bugs. E.g. you are calling button = this.LoginButton(), but LoginButton functions expects props. You either have to pass the props to the function or you can access it in function as this.props.
Also the way you did it is kind of antipattern, because you are defining multiple components inside App component (LogoutButton, LoginButton etc.). You should split them into multiple classes.
I solved this in a fairly simple way, keep in mind I have a back end with express that searches a predefined username and pass that is why I used axios to fetch that info and check against it in the onSubmit function.
FYI: if you're feeling lost with React or any language really just start by doing projects, that is how I am learning, I took a few Udemy courses but still didn't grasp it, I learned by doing and with each project(big or small) you pick up something new and/or gain a better understanding along the way. Just a little food for thought if you're like me and have a passion but don't know where or how to start.
class App extends Component {
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
user:[],
isLoggedIn:false,
username:'',
password:''
};
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
handleLogoutClick = e => {
this.setState({ isLoggedIn: false })
}
onSubmit = (e) => {
e.preventDefault();
axios.get('API_PATH')
.then(res => {
const user = res.data[0].username;
const password = res.data[0].password;
const username = this.state.username;
const passwordEntered = this.state.password;
if(user === username && passwordEntered === password){
this.setState({ isLoggedIn:true })
}
})
.catch(error => {
console.log(error);
});
}
render(){
return (
<div className="App">
<h4
className="display-4 mt-4 mb-4 text-center"
>
<i style={redColor}
className="fas fa-asterisk">
</i> Expiration Reminder
</h4>
<Container onSubmit={this.onSubmit}>
{this.state.isLoggedIn ? (
<>
<Button className="btn btn-sm btn-light mr-5 float-right" onClick={this.handleLogoutClick}>
<i style={redColor} className="fas fa-sign-out-alt"></i> Logout
</Button>
<ReminderModal />
<p className="text-muted text-center">If new reminder does not show up immediently please refresh page</p>
<ReminderTable />
</>
) : (
<>
<div id="status"></div>
<Form className="loginForm ml-5">
<Form.Row>
<Form.Group as={Col}>
<Form.Label>Username</Form.Label>
<Form.Control
type="text"
name="username"
id="username"
value={this.state.value}
onChange={this.handleChange}
>
</Form.Control>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
id="password"
name="password"
value={this.state.value}
onChange={this.handleChange}
/>
</Form.Group>
</Form.Row>
<Button className="btn btn-sm btn-light" onClick={this.onSubmit}>
<i style={redColor} className="fas fa-sign-in-alt"></i> Login
</Button>
</Form>
</>
)}
</Container>
</div>
)
}
}
export default App;