Add dynamic HTML in React JS? - javascript

Why is my code not working? I'm creating a registration form and I'm wanting to add an error message if the passwords do not match. Why is it not letting me dynamically add para tag to my html? Adding some more text here as I'm getting a post is mostly code error......
import React from 'react'
import Navbar from './components/Navbar'
import { Link } from 'react-router-dom'
import './Register.css'
import { useState, useRef } from 'react'
import { createUserWithEmailAndPassword } from "firebase/auth";
import { auth } from './firebase'
function Register() {
const div = useRef(null);
const handleSubmit = event => {
if (password == confirmPassword) {
createUserWithEmailAndPassword(auth, registerEmail, confirmPassword)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
}
else {
//problem
var passNotMatch = document.createElement('p');
passNotMatch.innerHTML = "Passwords do not match, please try again.";
div.appendChild(passNotMatch);
event.preventDefault();
}
}
return (
<>
<Navbar />
<div className='signup-div'>
<div useRef={div}>
<h2>Register</h2>
<form onSubmit={handleSubmit}>
<input className='input input_email' type="email" placeholder='Email Address' value={registerEmail} onChange={e => setRegisterEmail(e.target.value)} required /> <br />
<input className='input input_password' type="password" placeholder='Set password' value={password} onChange={e => setPassword(e.target.value)} required /> <br />
<input className='input input_password' type="password" placeholder='Confirm password' value={confirmPassword} onChange={e => setConfirmPassword(e.target.value)} required /> <br />
<button type='submit' className='register-button'>Register</button>
<Link to='/signin'>Already have an account? Sign In</Link>
</form>
</div>
</div>
</>
)
}

You're using React incorrectly. Directly interacting with the DOM is almost never the right approach in React. Instead, "dynamic" markup is conditionally included in the markup based on state values. For example, consider this markup structure:
return (
<>
<Navbar />
<div className='signup-div'>
<div>
<!-- the rest of your markup, then... -->
{showError ? <p>Passwords do not match, please try again.</p> : null}
</div>
</div>
</>
)
Note the conditional inclusion of the <p> element, based on the boolean value of showError. Which means showError is something you'd track in state:
function Register() {
const [showError, setShowError] = useState(false);
const handleSubmit = event => {
//...
}
//...
}
Its initial value is set to false, so the <p> won't be shown. Then you just update the state to true to show it:
else {
//problem
setShowError(true);
event.preventDefault();
}
You would also set it back to false wherever you want in your code. Perhaps at the beginning of the handleSubmit function for example.
Overall the concept is that you don't directly manipulate the DOM. Instead, you track the current "state" of things in state values. The rendering is based on the current state, and updates to the state trigger a re-render.

Related

How to Prevent useState & useLocation from re rendering in input/text field in react js?

So basically my problem is that I have two pages/screens (Main & Edit). In Main page when I click an item then it passes it to Edit page using useHistory hooks from react-router-dom package. In Edit page I get the item using useLocation hooks & pass them to input field for my initial useState, but every time I edit/type a character in text field not sure if its called re render or anything else, but the useLocation will be passed in console. Here is my code:
Edit Page/Screen
import axios from "axios";
import React, { useState } from "react";
import { useLocation } from "react-router";
export const EditScreen = () => {
const location = useLocation().state;
console.log(location);
const [title, setTitle] = useState(location.b_title);
const [content, setContent] = useState(location.b_content);
const [category, setCategory] = useState(location.category_id);
const submitHandler = (e) => {
e.preventDefault();
axios
.put(`http://localhost:3001/api/v1/blog/${location.id}`, {
blog_title: title,
blog_content: content,
category_id: category,
})
.then(alert("success edit blog"))
.catch((err) => alert(err));
setTitle("");
setContent("");
setCategory("");
};
return (
<div>
<h1>edit blog page</h1>
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<br />
<br />
<input
type="text"
placeholder="content"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<br />
<br />
<input
type="text"
placeholder="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
/>
<br />
<br />
<input
type="submit"
value="submit"
disabled={
title === "" || content === "" || category === "" ? true : false
}
/>
</form>
</div>
);
};
Screen Shot of Console
You can see above ss in red circle, thats the problem.
Bro, We cant prevent the setState from re-rendering the component as it's the react specified default before. So you must use the debouncing technique to prevent the multiple re-renders from happening https://www.telerik.com/blogs/debouncing-and-throttling-in-javascript u can go through this article for better clarity of how you can do that. It's mentioned with examples clearly in the article. You can also change the event to onBlur instead of onChange we reduce the number of setStates from getting called & we reduces the re-rendering.

React js - useState returns different values inside and outside of a onChange Form function

I would like to know why loginPassword.length and loginPasswordError is different inside and outside of loginFormPasswordHandler
import React, {useState} from 'react';
import './styles.css'
const App = () => {
const [loginPassword, setLoginPassword] = useState('');
const [loginPasswordError, setLoginPasswordError] = useState();
const [submitController, setSubmitController] = useState(false);
const loginFormSubmitHandler = (e) => {
e.preventDefault();
}
const loginFormPasswordHandler = (e) => {
setLoginPassword(e.target.value);
setLoginPasswordError(loginPassword.length < 8);
console.log('login password length is(inside):'+loginPassword.length+' and the state is '+loginPasswordError)
loginPassword.length > 8 ? setSubmitController(true) : setSubmitController(false);
}
console.log('login password length is(outside):'+loginPassword.length+' and the state is '+loginPasswordError)
return(
<React.Fragment>
<div className="form-wrapper">
<form onSubmit={loginFormSubmitHandler}>
<input className={`${loginPasswordError && 'error'}`} type="password" id="password" name="password" placeholder="Password" onChange={loginFormPasswordHandler} />
<div className={`submit-btn ${submitController ? '' : 'disable'}`}>
<input type="submit" />
</div>
</form>
</div>
</React.Fragment>
);
}
export default App;
I know useState re-runs the entire code when the state is changed. But I can't understand this behavior. I am not sure whether this is a Javascript property or React property.
setState is asynchronous, meaning your login password and error state values might not update immediately after you run setLoginPassword and setLoginPasswordError.
The other line below re-runs on every render, so it will output up to date values.
console.log('login password length is(outside):'+loginPassword.length+' and the state is '+loginPasswordError)

Run some code in React after multiple async useState setters

I have a functional React component in which I am using useState to manage state. Normally, it's just a form with two fields - code and env - which the user can manually fill out and submit. However, when the component loads, I also want to check any querystring params and if the appropriate ones exist, I want to populate and submit the form automatically. That way, users can bookmark specific form submissions.
The problem I'm having is that, as we all know, useState setters are async, just like setState in class components. As both form fields are controlled by state, setting both values will kick off multiple renders, so where should I put the code to submit the form so that I'm guaranteed that both state updates have completed?
Here is the form:
And here is a simplified, sanitized version of the code I have:
import React, { useState, useEffect } from "react";
import axios from "axios";
import queryString from "query-string";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import ToggleButtonGroup from "react-bootstrap/ToggleButtonGroup";
import ToggleButton from "react-bootstrap/ToggleButton";
import Card from "react-bootstrap/Card";
/*
* this component will show a spinner or the results from the API when complete
*/
const PortalDisplay = ({ data: portal, isLoading }) => {
if (Object.keys(portal).length === 0 && !isLoading) {
return null;
} else if (isLoading) {
return (
<div>
<p>loading…</p>
</div>
);
} else if (!!portal.id && !isLoading) {
return <div className="card-portal">data goes here</div>;
}
};
/*
* main component
*/
const PortalConfiguration = () => {
const [validated, setValidated] = useState(false);
const [code, setCode] = useState("");
const [env, setEnv] = useState("prod");
const [portalInfo, setPortalInfo] = useState({});
const [isLoading, setIsLoading] = useState(false);
const params = queryString.parse(location.search);
const onSubmitForm = (event) => {
const form = event.currentTarget;
setValidated(true);
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
return;
}
//re-initialize
setIsLoading(true);
setPortalInfo({});
axios
.get(`http://www.example.com/api`)
.then((response) => {
setIsLoading(false);
setPortalInfo({ ...response.data, ...{ baseUrl: baseUrl[env] } });
})
.catch((error) => console.log(error));
event.preventDefault();
event.stopPropagation();
};
useEffect(() => {
if (!!params && !!params.portal && !!params.env) {
if (!/^[a-zA-Z]{3}$/.test(params.portal) || (params.env !== "prod" && params.env !== "demo")) {
console.log(`Specified portal parameters {portal: ${params.portal}} and {env: ${params.env}} are incorrect`);
} else {
// this is where i want to set the portal and env states and submit the form
}
}
}, [params.portal]);
return (
<>
<h1>Your Portal Configuration</h1>
<Card>
<Card.Body>
<Form noValidate validated={validated} inline className="form-portal" onSubmit={onSubmitForm}>
<Form.Group className="form-group-code">
<label className="sr-only" htmlFor="code">
Portal Code
</label>
<Form.Control
type="text"
id="code"
value={code}
required
placeholder="Enter Portal Code (e.g. 'FWU')"
maxLength="3"
onChange={(e) => setCode(e.target.value)}
/>
<Form.Control.Feedback type="invalid">Portal Code must be three letters (a-z)</Form.Control.Feedback>
</Form.Group>
<Form.Group>
<ToggleButtonGroup type="radio" name="env" value={env} onChange={(val) => setEnv(val)}>
<ToggleButton type="radio" name="env" value="prod" variant="primary" className="btn-inline">
Production
</ToggleButton>
<ToggleButton type="radio" name="env" value="demo" variant="primary" className="btn-inline">
Demo
</ToggleButton>
</ToggleButtonGroup>
</Form.Group>
<Button variant="secondary" block="true" className="btn-inline" type="submit">
Submit
</Button>
</Form>
</Card.Body>
</Card>
<PortalDisplay data={portalInfo} isLoading={isLoading} env={env} />
</>
);
};
export default PortalConfiguration;
The line which is commented out and says "this is where i want to set the portal and env states and submit the form" is where I know I have querystring params passed and need to set both states, then submit the form.
FWIW, I have considered the usual answer to the question of how to deal with useState's asynchronicity, which is to handle it in useEffect, scoped to the particular state variable you are interested in. The two problems with that is that 1) I have two state variables that need to be updated so I don't think there's any guarantee that they will be updated in the order I called the setters, creating a possible race condition, and 2) I don't want to call this code every time that code or env updates, which can happen when the user manually interacts with the form. I only want it to be auto-submitted when the component detects the querystring upon loading.

How to disable submit button in redux-form

I am working on a login page where I am using redux-form. I want to disable the submit button until email and password are filled. I tried but I am failed, could someone please help me how to achieve my goal. Thanks
Code
<form onSubmit={handleSubmit}>
<div className="sign-up-form">
<div className="space-2">
<Field
name="email"
component={renderField}
type="email"
label="Email"
/>
</div>
<div className="space-2">
<Field
name="password"
component={renderField}
type="password"
label="Password"
/>
</div>
{/* <button className='login-button' type='submit'>Login</button> */}
<div className="">
<button className="login-button" type="submit">
{loading ? (
<Loader
type="ThreeDots"
color="#ffffff"
height="10"
width="100"
/>
) : (
"Login"
)}
</button>
</div>
</div>
</form>
You can check this link handleSubmit and props:
https://redux-form.com/6.0.0-alpha.4/docs/api/props.md/
const {invalid} = this.props
return(
<button type="submit" className="send-btn"
disabled={invalid|| submitting || pristine}>
submit
</button>)
A possible way of doing this is use redux-form selectors to read the input values and return a property indicating if the button should be enabled or not.
To do so, you need to connect your form to redux state and use mapStateToProps to return the desired value.
Idea:
import { connect } from "react-redux";
import { Field, reduxForm, formValueSelector } from "redux-form";
let MyForm = props => {
const { enableSubmit } = props; // new property set from redux state
return (
<form>
... your form
</form>
}
const selector = formValueSelector("myForm"); // <-- same as form name
MyForm = connect(state => {
const hasUsername = selector(state, "email"); // read username value
const hasPassword = selector(state, "password"); // read username value
const enableSubmit = hasUsername && hasPassword; // logic for enabling the submit button
return {
enableSubmit // this will set property `enableSubmit` which you can read in your component
};
})(MyForm);
I prepared a working example here

Referencing Function from another ReactJS Page for Validation

I am currently writing my first React Project for a class assignment. I am trying to make a login page that navigates to a new dashboard page. I do not want any fancy security, so I wanted it just to have "if password === this password then go to dashboard, if not then error message.
I have the button working fine without validation, and I have my handlers for the text input working as I can display what is typed by using this.state.username and this.state.password in my login-form.js file.
The problem I can't figure out is how to reference/use those states in my login-button.js file so I can create that if statement validator? Can anyone help?
Here is my login-form.js file:
import React from 'react';
import "./login-form.css";
import logo from './../../logo-beesecure-2-tm.png';
import Login_btn from './../login-button/login-button';
class Login_Form extends React.Component {
constructor(props){
super(props);
this.state = { username: '', password: '' };
}
handleChange = ({ target }) => {
this.setState({ [target.name]: target.value });
};
render() {
return (
<div className='login-container'>
<img src={logo} className="App-logo" alt="logo" />
<p>LOGIN</p>
<form onSubmit="" className="login-form">
<input
type="text"
placeholder="Username"
name="username"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="password"
placeholder="Password"
name="password"
value={this.state.password}
onChange={this.handleChange}
/>
</form>
<Login_btn />
<h2>Your username is: {this.state.username}</h2>
<h2>Your password is: {this.state.password}</h2>
</div>
);
}
}
export default Login_Form;
And here is my login-button.js file:
import './login-button.css';
import React from 'react';
import { useHistory } from "react-router-dom";
import Login_Form from '../login-form/login-form';
function Login_btn() {
let history = useHistory();
function handleClick() {
history.push("/dashboard");
}
return (
<button className="Login-Button" onClick={handleClick}>Login</button>
);
}
export default Login_btn;
Thank you in advance!
You can pass in the states from your <Login_Form /> into your <Login_btn /> by using props like so:
<Login_btn username={this.state.username} password={this.state.password} />
Then you can reference the props in your <Login_btn />:
function Login_btn(props) {
let history = useHistory();
function handleClick() {
const { username, password } = props;
history.push("/dashboard");
}
return (
<button className="Login-Button" onClick={handleClick}>Login</button>
);
}
You can read more about props here: https://reactjs.org/docs/components-and-props.html

Categories

Resources