Conditional Rendering not working in React - reactjs

I'm trying to do achieve just a simple login form for an app but for some reason cannot get my ternary operator to work. I'm simply importing the login credentials using a json file and using a submit handler to compare the user input with the data using a ternary. On submit, nothing happens. See my code below:
import React from 'react'
import customData from '../db.json'
import DocumentPage from './DocumentPage'
class MemberPortal extends React.Component {
state = {
username: "",
password: "",
}
handleUserChange = (event) => {
this.setState({username: event.target.value});
}
handlePwChange = (event) => {
this.setState({password: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
return this.state.password == customData.password ? <DocumentPage /> : alert("incorrect login information")
}
render(){
return(
<div id ="test">
<form onSubmit = {()=>this.handleSubmit}>
<label for="username">User Name:</label>
<div className="ui input">
<input type="text" id="username" value = {this.state.username} onChange = {this.handleUserChange} placeholder = "Username"></input>
</div>
<label for="password">Password:</label>
<div className="ui input">
<input type="text" id="password" value = {this.state.password} onChange = {this.handlePwChange} placeholder="Password"></input>
</div>
<input className="ui blue button" type="submit" value="Submit" onClick = {this.handleSubmit}></input>
</form>
</div>
)
}
}
export default MemberPortal;

handleSubmit is an event handler, it's a function that will be triggered when you click the button, you are not supposed to return a JSX element or anything from it. A good practice is to avoid return anything from an event handler to avoid confusion.
Generally speaking, and if you are familiar with static type language such as Typescript, an event handler should have the return type as void.
As other people have already pointed out, if you can redirect to another URL if the login is successful, or if you want to do some conditional rendering within the same component, you can set a state indicating that the login is success.
A code example can be:
class MemberPortal extends React.Component {
state = {
username: "",
password: "",
isLoginSuccessful: false
}
handleSubmit = (event) => {
event.preventDefault();
// perform login function here
....
// Login not success, alert or do anything you like
if (this.state.password !== customData.password) {
this.setState({ isLoginSuccessful: false });
alert("incorrect login information");
return;
}
// Login success, perform redirect or set the boolean flag to true for conditional rendering
this.setState({ isLoginSuccessful: true });
}
....
}

Related

How do I pass input values to a new div in React?

I'm working on a CV Generator and I don't know how to properly append the school and field of study values to a new div inside React.
Using the onSubmit function I'm able to get the values after filling them out and clicking save, but I can't figure out where to go from here.
Update
What I want to do is take the values from the input and create a new div above the form that displays those values. For example, I want the School value to show
School: University of Whatever
And the same goes for Field of Study.
Field of Study: Whatever
I know how to do this in vanilla JS but taking the values and appending them to the DOM but it doesn't seem to work that way in React.
class Education extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = (e) => {
e.preventDefault();
const schoolForm = document.getElementById("school-form").value;
const studyForm = document.getElementById("study-form").value;
};
render() {
return (
<>
<h1 className="title">Education</h1>
<div id="content">
<form>
<label for="school">School</label>
<input
id="school-form"
className="form-row"
type="text"
name="school"
/>
<label for="study">Field of Study</label>
<input
id="study-form"
className="form-row"
type="text"
name="study"
/>
<button onClick={this.onSubmit} className="save">
Save
</button>
<button className="cancel">Cancel</button>
</form>
)}
</div>
</>
);
}
}
export default Education;
You should use state in order to save the values then show it when the user submits.
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { scool: "", study: "", showOutput: false };
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = (e) => {
e.preventDefault();
this.setState({
showOutput: true
});
};
setStudy = (value) => {
this.setState({
study: value
});
};
setSchool = (value) => {
this.setState({
school: value
});
};
render() {
return (
<>
<h1 className="title">Education</h1>
<div id="content">
{this.state.showOutput && (
<>
<div>{`school: ${this.state.school}`}</div>
<div>{`study: ${this.state.study}`}</div>
</>
)}
<form>
<label for="school">School</label>
<input
id="school-form"
className="form-row"
type="text"
name="school"
onChange={(e) => this.setSchool(e.target.value)}
/>
<label for="study">Field of Study</label>
<input
id="study-form"
className="form-row"
type="text"
name="study"
onChange={(e) => this.setStudy(e.target.value)}
/>
<button onClick={this.onSubmit} className="save">
Save
</button>
<button className="cancel">Cancel</button>
</form>
)
</div>
</>
);
}
}
export default App;
I have also added 2 functions to set state and a condition render based on showOutput.
You don't append things to the DOM in react like you do in vanilla. You want to conditionally render elements.
Make a new element to display the data, and render it only if you have the data. (Conditional rendering is done with && operator)
{this.state.schoolForm && this.state.studyform && <div>
<p>School: {this.state.schoolForm}</p>
<p>Field of Study: {this.state.studyForm}</p>
</div>}
The schoolForm and studyForm should be component state variables. If you only have them as variables in your onSubmit, the data will be lost after the function call ends. Your onSubmit function should only set the state, and then you access your state variables to use the data.
Do not use document.getElementById. You don't want to use the 'document' object with react (Almost never).
You can access the element's value directly using the event object which is automatically passed by onSubmit.
handleSubmit = (event) => {
event.preventDefault();
console.log(event.target.school.value)
console.log(event.target.study.value)
}

Password show/hide using Eye/EyeSlash in React

I am trying to implement eye/eyeslash in on my Register form in React.
This is a function that's is responsible for changing visibility type and eye icon changing.
import React, { useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
export const usePasswordToggle = () => {
const [visible, setVisibility] = useState();
const Icon = <FontAwesomeIcon icon={visible ? "eye-slash" : "eye"} />;
const InputType = visible ? "text" : "password";
return [InputType, Icon];
};
I am trying to implement it in component responsible for registering.
import React, { Component, createRef } from "react";
import { usePasswordToggle } from "./usePasswordToggle";
class Register1 extends React.Component {
EmailR = createRef();
UsernameR = createRef();
PasswordR = createRef();
PasswordConfirmR = createRef();
constructor(props) {
super();
this.state = {
message: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = (event) => {
// alert(this.PasswordR.current.value);
// alert(this.PasswordConfirmR.current.value);
if (this.PasswordR.current.value !== this.PasswordConfirmR.current.value) {
alert("The passwords doesn't match");
return false; // The form won't submit
} else {
alert("The passwords do match");
return true; // The form will submit
}
};
onCreateAccount = () => {
let loginInfo = {
Username: this.UsernameR.current.value,
Email: this.EmailR.current.value,
Password: this.PasswordR.current.value,
};
fetch("http://localhost:5000/api/authenticate/register", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(loginInfo),
})
.then((r) => r.json())
.then((res) => {
if (res) {
this.setState({
message:
"New Account is Created Successfully. Check your email to verify Account.",
});
}
});
};
render() {
return (
<div>
<h2 className="FormDescription">
{" "}
Please enter Account details for registration
</h2>
<div className="Form">
<p>
<label>
Email: <input type="text" ref={this.EmailR} />
</label>
</p>
<p>
<label>
Username: <input type="text" ref={this.UsernameR} />
</label>
</p>
<div>
<label>
Password:{" "}
<input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">
{usePasswordToggle.Icon}
</span>
</div>
<p>
<label>
ReenterPassword:{" "}
<input type="password" ref={this.PasswordConfirmR} />{" "}
</label>
</p>
<button onClick={this.handleSubmit}> Create </button>
<p>{this.state.message}</p>
</div>
</div>
);
}
}
export default Register1;
My password is always visible, and eye icon is even not visible on the form (it should be inside my input field, but it is not).
Focus on this code snippet:
<div>
<label>
Password: <input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">{usePasswordToggle.Icon}</span>
</div>
Any suggestion what is the problem?
Change this
const [visible, setVisibility] = useState();
to this
const [visible, setVisible] = useState(true);
as the official documentation here
First, add a default value to your useState, either true or false depending on which icon you want to render first.
Then, you should add a onClick method to your icon which will toggle the visibility state. You're setting the icon based on visible value, but you never toggle the value.
onClick={() => setVisibility(!visible)}
UPDATE
You also need to execute your Hook inside your main component (because yes, you wrote what React call a Hook), like so :
const [inputType, icon] = usePasswordToggle();
But doing so, you'll get an error from React that say you cannot use a Hook within a class component due to how they work.
Basically you need to change your Register1 component to be a functional component, and not a class anymore. Look here for a quick overview on how to : https://reactjs.org/docs/components-and-props.html

How can I clear the log in error messages when the user doesn't refresh the page and simply closes/exits out of my modal?

I've made a modal for a simple log in page for a website:
import React from 'react';
import { withRouter } from 'react-router-dom';
import '../../assets/stylesheets/session/login_form.css';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errors: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.renderErrors = this.renderErrors.bind(this);
this.handleDemo = this.handleDemo.bind(this);
}
// After authentication redirect user to home page
componentWillReceiveProps(nextProps) {
if (nextProps.currentUser === true) {
this.props.history.push('/');
this.props.closeModal();
}
// Setting or clearing errors
this.setState({ errors: nextProps.errors });
}
// Hides scrolling when modal is mounted
componentDidMount() {
if (this.props.modal) document.body.style.overflow = 'hidden';
}
// Reactiviates scrolling when modal is unmounted
componentWillUnmount() {
document.body.style.overflow = 'unset';
}
// Render the session errors if there are any
renderErrors() {
return (
<ul>
{Object.keys(this.state.errors).map((error, i) => (
<li key={`error-${i}`}>{this.state.errors[error]}</li>
))}
</ul>
);
}
// Handle field updates
update(field) {
return e =>
this.setState({
[field]: e.currentTarget.value
});
}
// Handle form submission
handleSubmit(e) {
e.preventDefault();
let user = {
email: this.state.email,
password: this.state.password
};
if (this.props.errors) {
this.props.login(user)
.then(() => this.props.openModal('login'));
} else {
this.props.login(user)
.then(() => this.props.closeModal());
}
}
// Handle demo user login
handleDemo(e) {
e.preventDefault();
const user = { email: 'demouser#nookbnb.com', password: 'password' };
this.props.login(user)
.then(this.props.history.push('/'), this.props.closeModal());
}
// Rendering component
render() {
let errors;
if (this.props.errors) {
errors = this.props.errors;
} else {
errors = {};
}
let emailErrors = errors.email ? <div className="email-error">{errors.email}</div> : '';
let passwordErrors = errors.password ? <div className="password-error">{errors.password}</div> : '';
return (
<div className="login-modal-wrapper">
<div className="modal-wrapper" onClick={this.props.closeModal}></div>
<form onSubmit={this.handleSubmit}>
<div className="header-wrapper">
<div className="close-wrapper" onClick={this.props.closeModal}>
<i className="close-button"></i>
</div>
<h1>Log in</h1>
</div>
<div className="main-content-wrapper">
<button onClick={this.handleDemo}>
Demo Log in
</button>
<div className="button-separator-wrapper"><p>or</p></div>
<input
type="text"
value={this.state.email}
onChange={this.update('email')}
placeholder="Email"
/>
<input
type="password"
value={this.state.password}
onChange={this.update("password")}
placeholder="Password"
/>
<div className="session-errors">
{emailErrors}
{passwordErrors}
</div>
<button type="submit">Log in</button>
<div className="no-account-wrapper">
<p>Don't have an account? <span onClick={() => this.props.openModal('signupFirst')}>Sign up</span></p>
</div>
</div>
</form>
</div>
);
}
}
export default withRouter(LoginForm);
And I've successfully displayed the right error messages when the user doesn't enter a required field in the login form (an email and a password), but if I don't manually do a page refresh, the errors still appear on the form even after I close and reopen the modal.
How can I implement this modal in a way where it will automatically clear errors after I close and reopen the modal?
UPDATE
Per the answer below, I've added these two open and closing modal functions to help clear the errors:
// Opens a login modal
openLoginModal() {
this.setState({ errors: {} });
this.props.openModal('login');
}
// Closes a login modal
closeLoginModal() {
this.setState({ errors: {} });
this.props.closeModal();
}
And I've replaced places in the render where I'm using this.props.closeModal() and this.props.openModal() with my functions above. (For now I'm just testing this with closing the modal; since the modal doesn't have any errors when initially opened, I believe I just need to account for closing the modal right now)
<div className="login-modal-wrapper">
<div className="modal-wrapper" onClick={this.closeLoginModal}></div>
<form onSubmit={this.handleSubmit} className={errors.email && errors.email.length !== 0 ? 'form-errors' : 'form-normal'}>
<div className="header-wrapper">
<div className="close-wrapper" onClick={this.closeLoginModal}>
<i className="close-button"></i>
</div>
...
But error messages are still persisting when I open and close the modal.
Perhaps consider having an openLoginModal method that clears any errors and then opens the modal:
openLoginModal() {
this.setState({ errors: {} })
this.props.openModal('login');
}
And then replace any occurrence of this.props.openModal('login') to use this new method (this.openLoginModal()).
Edit: If you need to clear the errors specifically on exiting the modal, you can do a similar thing by creating a custom method around the closeModal prop.

How do i render a component after a password is submitted?

I have components in my react single page web app that require a password to view. Upon clicking the 's, a password form component is rendered. I have the logic written to check if the password is correct. If it is correct, how do I then leave the password form component, and render the component that the link was originally headed to?
I've tried just toggling visibility but I think I'm mostly confused on how specifically to use React Router to render a component on the condition that the password was correct
Parent Component
handleClick = (e) => {
e.preventDefault();
this.setState({ isPasswordVisible: !this.state.isPasswordVisible });
}
render() {
return (
<div className="BigNames">
<Link onClick={this.handleClick} className="BigNames-link" to='/Adobe' style={{textDecoration:'none'}}>
<span className='Name'>Adobe Creative Cloud</span>
<span className='Text'>: App Banner</span> <i className="fas fa-lock"></i>
</Link>
Password Component
import React, { Component } from 'react';
import './Password.css';
import Adobe from './Big Work/Adobe';
export default class Password extends Component {
static defaultProps = {
password: 'pierpoint'
}
constructor(props) {
super(props)
this.state = {
visible: true,
value: ''
}
this.handleClick = this.handleClick.bind(this)
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {
e.preventDefault();
this.setState({value: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
if(this.state.value === this.props.password) {
alert('Correct!');
this.setState({visible: false});
return( <Adobe />)
} else (alert('Incorrect Password!'))
}
handleClick(event) {
event.preventDefault()
this.setState(prevState => ({
visible: !prevState.visible,
}))
}
render() {
if (!this.state.visible) {
return null
}
return (
<div className="pwd">
<div className="enter-pwd">
<button className='exit' onClick={this.handleClick}> ✕ </button>
<form onSubmit={this.handleSubmit}>
<input
className="sub-text"
type='password'
name='password'
placeholder='Enter password'
value={this.state.value}
onChange={this.handleChange}>
</input>
<button
className="sub-mit"
type='submit'>
submit
</button>
</form>
</div>
</div>
)
}
}
The password component does go away after a correct password is submitted, however the following conditional component doesn't render.s
Here is the codepen showing the full example : Hidden by password page
In my example, the hidden page is a component I called SecretPage and the form handling the password is called Password. The parent component is App.
Because I needed to know inside of App whether the password was correct or not, the first step was to make Password a controlled component.
function Password(props){
return (
<div>
<p>Maybe the secret is a potato ?</p>
<form onSubmit={props.onSubmit}>
<input type='password' value={props.password} onChange={props.onChange}/>
<input type='submit' value='submit'/>
</form>
</div>);
}
What that means is simply that onSubmit, onChange and the value of password input itself are all given as props, and are handled by App and not by Password itself.
Here is how Password is called inside the App function
<Password password={this.state.password} onChange={this.handleChange} onSubmit={this.handleSubmit} />
Whenever the form is submitted, the function handleSubmit from App is called and it looks like this:
handleSubmit(e){
e.preventDefault();
this.setState({
secretVisible : this.checkPassword(this.state.password),
});
}
Because secretVisible is a state of the App now, knowing which page it should display is really easy. It only needs to check the this.state.secretVisible.
render(){
const secretVisible = this.state.secretVisible;
let pageToDisplay;
if(secretVisible){
pageToDisplay = <SecretPage onTakeMeBackClicked={this.handleLogOff}/>;
}
else{
pageToDisplay = <Password password={this.state.password} onChange={this.handleChange} onSubmit={this.handleSubmit} />;
}
return (
<div>
{pageToDisplay}
</div>
);
}
}
There are several ways to handle it. You can F.E use protected route togheter with react-router or in a simple case you can:
class App extends Component {
state = {
isAuthenticated: false,
}
setIsAuthenticated = (bool) => {
this.setState({isAuthenticated: bool})
}
render(){
const { isAuthenticated } = this.state;
return(
if(isAuthenticated){
return <YourMainComponent />
)
return <AuthFormComponent setIsAuthenticated={this.setIsAuthenticated} />
}
}
This is just an example, but I hope it gives you a tip on how to handle it.
There's not a ton of code here to give a proper example, but in pseudocode you'll want to do something like:
<div>
{isPasswordVerified
? <ComponentYouWantToShow />
: <Password callbackProp={setIsPasswordVerified} />
}
</div>
The Password component needs a callback prop to send whether the verification was successful to the parent component. Then in the parent component you can conditionally render the appropriate component. No need to deal w/ Routing here.

Calling a function for a generate button

I have a page where a user can search a database for a given condition, then the data is returned with another button that the user can use to add information back to the database. However whenever I click on the second button, the page reloads. I can't get so much as a console.log to go in. I'm new to react and could use any help at all.
import React , { Component } from 'react';
import { database } from '../firebase';
const byPropKey = (propertyName, value) => () => ({
[propertyName]: value,
});
class Search extends Component{
constructor(props) {
super(props);
this.state={
users: null,
searchCondition: "",
friend: ""
}
// this.setState = this.setState.bind(this);
}
onSubmit = (event) => {
let {
searchCondition,
friend
} = this.state;
database.searchConditions(searchCondition).then(snapshot =>
this.setState(() => ({ users: snapshot.val() }))
);
event.preventDefault();
}
messageSubmit = (event) => {
console.log("Click")
}
render(){
let {
users,
searchCondition,
friend
} = this.state;
return(
<div>
<h1>Search for conditions</h1>
<form onSubmit={this.onSubmit}>
<div className="search">
<input
value={searchCondition}
onChange={event => this.setState(byPropKey('searchCondition', event.target.value))}
type="text"
placeholder="Condition to Search For"
/>
<button className="friendButton"
onClick="x"
type="submit">
Search
</button>
</div>
</form>
{!!users && <UserList users={users} />}
</div>
)
}
}
let UserList = ({ users, message }) =>
<div>
<h2>List of Usernames and Conditions of your Search</h2>
{Object.keys(users).map(key =>
<div key={key}>{users[key].username} : {users[key].condition}
<form>
<div className="search">
<input
value={message}
onChange={console.log("test")}
type="text"
placeholder="Message for this User"
/>
<button className="messageButton"
onClick={console.log(message)}
type="submit">
Message
</button>
</div>
</form>
</div>
)}
</div>
export default Search;
Have you tried to place the event.preventDefault() at the beginning of the event handler?
It should prevent the default behaviour imediately as the event gets fired.
Hope it works!
a couple things i can see, youre even.preventDefault() should be at the top of the page, you said it was reloading so thats unwanted behavior. second you should set state within the then, generally speaking in my experience that doesnt work- i believe due to setState being asynchronous or something of that nature.
i would rewrite your submit like this
onSubmit = (event) => {
event.preventDefault();
let {
searchCondition,
friend
} = this.state;
let value;
database.searchConditions(searchCondition).then(snapshot =>
value = snapshot.val
);
this.setState(() => ({ users: value) }))
}
also likely the reason your "messageSubmit()" was not console logging is because youre using a submit handler not a click handler so everytime your clicked you were reloading the page.
cheers

Resources