Referencing Function from another ReactJS Page for Validation - javascript

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

Related

Add dynamic HTML in React JS?

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.

React Final Form Error: Must specify either a render prop

I am trying to build a simple form with React-Final-Form like this:
import * as React from "react";
import {
PrimaryButton,
} from "office-ui-fabric-react/lib/Button";
import { Form , Field } from "react-final-form";
import { FORM_ERROR } from "final-form";
import { IUserFormValues } from "../../models/user";
import { RootStoreContext } from "../../stores/rootStore";
import TextInputNew from "./TextInputNew";
const NewUIForm = () => {
const rootStore = React.useContext(RootStoreContext);
const { login } = rootStore.userStore;
return (
<Form
onSubmit={(values: IUserFormValues) =>
login(values).catch((error) => ({
[FORM_ERROR]: error,
}))
}
render={({
handleSubmit,
}) => (
<Form onSubmit={handleSubmit}>
<Field name="email" component={TextInputNew} />
<Field name="email" component={TextInputNew} />
<PrimaryButton type='submit' text="Save" />
</Form>
)}
/>
);
};
export default NewUIForm;
The TextInputNew Component is this:
import * as React from "react";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { FieldRenderProps } from "react-final-form";
interface IProps extends FieldRenderProps<string, HTMLInputElement> {}
const TextInputNew: React.FC<IProps> = ({ input }) => {
return (
<div>
<input {...input} />
<TextField label="Standard" />
</div>
);
};
export default TextInputNew;
Then I got this error when I use this NewUIForm component
Error: Must specify either a render prop, a render function as children, or a component prop to ReactFinalForm
By the way, the UI framework is Fluent-UI
Can anyone help me? Thanks!!
You're second <Form> should be <form>.
<form onSubmit={handleSubmit}>
<Field name="email" component={TextInputNew} />
<Field name="email" component={TextInputNew} />
<PrimaryButton type='submit' text="Save" />
</form>
To anyone else who might encounter this vague error message, the issue is something going wrong in the render function of <Form>.
For OP, it was using the wrong form tag inside of <Form>.
For me, it was a misspelled property on a <Field> component (components={MyComponent}, oops).
Since the error can be caused by any number of reasons and the message wasn't very specific, one can get an idea of where the problem might be via browser debugger, in my case this is what it looked like:

React - How to get state data from a child component?

I am just starting to learn react and I'm currently building a form, so far I've created a parent component 'Form' and I've separated the rest of the inputs as components and each component has its own state. My question is how to get that state data from the children's components and use it in the parent component 'Form' when submitting the form?
Here is my parent component
import React, { Component } from "react";
import Name from "components/Name";
import Email from "components/Email";
import Select from "components/Select";
import Bio from "components/Bio";
class Form extends Component {
handleSubmit = event => {
event.preventDefault();
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="shape rectangle"></div>
<div className="shape triangle"></div>
<div className="shape circle"></div>
<Name />
<Email />
<Select />
<Bio />
<button type="submit" className="btn">
Submit
</button>
</form>
);
}
}
export default Form;
And one of the child component
import React, { Component } from "react";
// Create Name component for name && email inputs
class Name extends Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: ""
};
}
// Handle first name input on change event
handleFirstNameChange = event => {
this.setState({
firstName: event.target.value
});
};
// Handle last name input on change event
handleLastNameChange = event => {
this.setState({
lastName: event.target.value
});
};
// Render labels and name inputs
render() {
const { firstName, lastName } = this.state;
return (
<div className="form-names">
<label htmlFor="firstName">Name</label>
<br/>
<input
type="text"
name="firstName"
value={firstName}
placeholder="First Name"
id="firstName"
onChange={this.handleFirstNameChange}
/>
<input
type="text"
name="lastName"
value={lastName}
placeholder="Last Name"
id="lastName"
onChange={this.handleLastNameChange}
/>
</div>
);
}
}
export default Name;
To accomplish this you would need to "Lift the state" up to a common parent component (known as ancestor component), in your case, this would be the <Form> component. Then you would pass down the values to each corresponding child component as props.
It would look something like this:
import React, { Component } from "react";
import Name from "./Name";
// More imports go here..
class Form extends Component {
state = {
firstName: "",
lastName: ""
};
handleSubmit = event => {
event.preventDefault();
};
// Handle first name input on change event
handleFirstNameChange = event => {
this.setState({
firstName: event.target.value
});
};
// Handle last name input on change event
handleLastNameChange = event => {
this.setState({
lastName: event.target.value
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<Name
firstName={this.state.firstname}
lastName={this.state.lastName}
handleFirstNameChange={this.handleFirstNameChange}
handleLastNameChange={this.handleLastNameChange}
/>
{/* More components go here.. */}
<p>Current state:</p>
{JSON.stringify(this.state)}
</form>
);
}
}
export default Form;
Working example
More info: Lifting state up from the official React docs.
you can use a single state in the parent component , and pass the firstname and lastname in the props, your code will became:
import React, { Component } from "react";
import Name from "components/Name";
import Email from "components/Email";
import Select from "components/Select";
import Bio from "components/Bio";
class Form extends Component {
state = {
firstName: '',
lastName: '',
}
onChange = (e) => {
this.setState({[e.target.name] : e.target.value})
}
handleSubmit = event => {
event.preventDefault()
}
render() {
return (
...
<Name onChange={this.onChange} firstName={this.state.firstName} lastName={this.state.lastName} />
...
<button type="submit" className="btn">
Submit
</button>
</form>
)
}
}
export default Form;
and your child component became:
import React, { Component } from "react";
// Create Name component for name && email inputs
class Name extends Component {
render() {
const { firstName, lastName , onChange} = this.props;
return (
<div className="form-names">
<label htmlFor="firstName">Name</label>
<br/>
<input
type="text"
name="firstName"
value={firstName}
placeholder="First Name"
id="firstName"
onChange={onChange}
/>
<input
type="text"
name="lastName"
value={lastName}
placeholder="Last Name"
id="lastName"
onChange={onChange}
/>
</div>
);
}
}
export default Name;
My thought is to do sth like the following, passing the data as props to the component. But it doesn't make sense to me as when you click the submit button, the form gather data from the parent component. It may better keep the form in the parent component and group all the input data in state.
import React, { Component } from "react";
import Name from "components/Name";
import Email from "components/Email";
import Select from "components/Select";
import Bio from "components/Bio";
class Form extends Component {
constructor(props){
super(props)
this.state = {
firstName = "",
lastName = "",
(and other input data you need)...
}
}
handleSubmit = event => {
event.preventDefault();
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="shape rectangle"></div>
<div className="shape triangle"></div>
<div className="shape circle"></div>
<Name firstName={this.state.firstName} lastName={this.state.lastName}/>
<Email (inputData as props)/>
<Select (inputData as props)/>
<Bio (inputData as props)/>
<button type="submit" className="btn">
Submit
</button>
</form>
);
}
}
export default Form;
You could either use something like Context or Redux, which would allow you to create a universal store and retrieve state from any component or create methods in your parent component, pass them down to it's children, and invoke them from within that child component. For example, you could have handleFirstNameChange in Form and pass it down to Name. In doing so you'd now keep props like name in Form as well and pass that down as well.

Why changing state from false to true throws a Warning

I am creating a simple client-side login form. When a user logs in the state currentAdmin changes to true (it's false by default). By some reason, setState throws a Warning A component is changing an uncontrolled input of type submit to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
I tried using refs in Header.js but this didn't help.
App.js
import React, { Component } from 'react';
import Header from '../Header/Header';
class App extends Component {
constructor(props) {
super(props)
this.state = {
currentAdmin: false
}
}
onSignIn = (login, password) => {
if (login === 'admin' && password === '123') {
this.setState({
currentAdmin: true
})
}
}
onSignout = () => {
this.setState({
currentAdmin: false,
})
}
render() {
return (
<div>
<Header
onSignin={this.onSignIn.bind(this)}
onSignout={this.onSignout.bind(this)}
currentAdmin={this.state.currentAdmin}
/>
</div>
)
}
}
export default App.js;
Header.js
import React, {Component} from 'react';
import './Header.css';
class Header extends Component {
constructor(props) {
super(props);
this.handleSignin = this.handleSignin.bind(this);
this.handleSignout = this.handleSignout.bind(this);
}
handleSignin(e) {
e.preventDefault();
let login = this.refs.login.value;
let password = this.refs.password.value;
this.props.onSignin(login, password);
}
handleSignout(e) {
e.preventDefault();
this.props.onSignout();
}
render() {
if (this.props.currentAdmin === false) {
return (
<div className='header'>
<a href='google.com' className='logo'>ToDoApp</a>
<form className='login' onSubmit={this.handleSignin}>
<input type='text' placeholder='login' ref='login'/>
<input type='password' placeholder='password' ref='password'/>
<input type='submit' value='Log in'/>
</form>
</div>
);
} else {
return (
<div className='header'>
<a href='google.com' className='logo'>ToDoApp</a>
<form className='login' onSubmit={this.handleSignout}>
<input type='submit' value='Log out'/>
</form>
</div>
);
}
}
}
export default Header;
This throws a Warning.
You need to use defaultValue property on input instead of value. Check docs here.
As other option - you may want to replace input type="submit" with button type="submit"
ALexand Tovmach provided perfect (personally for me) answer. I should have changed <input type='submit' value='Log in/>to <button type='submit'>Log in</button>

is this valid for getting input state value?

I'm trying to build reusable form and input components and so far was able to achieve what I am after, here is how they are used in a LoginPage component
import React from 'react'
import classNames from 'classnames/bind'
import styles from './style.scss'
import Form from 'components/Form'
import Input from 'components/Input'
const cx = classNames.bind(styles)
export default class LoginPage extends React.Component {
constructor (props) {
super(props)
this.loginUser = this.loginUser.bind(this)
}
loginUser () {
console.log(`
Email: ${this.refs.email.state.value} Password: ${this.refs.password.state.value}
`)
}
render () {
return (
<main className={cx('LoginPage')}>
<div className={cx('container')}>
<Form onSubmit={this.loginUser}>
<Input type='email' placeholder='email' ref='email' />
<Input type='password' placeholder='password' ref='password' />
<Input type='submit' value='Log in' />
</Form>
</div>
</main>
)
}
}
The question I'm asking is if using this.refs.email.state.value is a valid approach to this, to get that input components value from its state?
I don't think you need refs for your case. Remember that they should not be used when the normal data flow of props can accomplish the same. From the docs
If you have not programmed several apps with React, your first
inclination is usually going to be to try to use refs to "make things
happen" in your app. If this is the case, take a moment and think more
critically about where state should be owned in the component
hierarchy. Often, it becomes clear that the proper place to "own" that
state is at a higher level in the hierarchy. Placing the state there
often eliminates any desire to use refs to "make things happen" –
instead, the data flow will usually accomplish your goal.
Another warning sign should be that string refs are considered legacy and will be deprecated.
A better and safer way of achieving what you need is to handle the input change separately and keep that in the local component state. So your code would look something like this:
// ...imports...
const cx = classNames.bind(styles)
const initialState = {
eamil: '',
password: '',
};
export default class LoginPage extends React.Component {
constructor (props) {
super(props);
this.loginUser = this.loginUser.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
loginUser () {
e.preventDefault();
console.log(`
Email: ${this.state.email} Password: ${this.state.password}
`)
}
render () {
return (
<main className={cx('LoginPage')}>
<div className={cx('container')}>
<Form onSubmit={this.loginUser}>
<Input
type='email'
value={this.state.email}
placeholder='email'
onChange={this.handleChange}
/>
<Input
type='password'
value={this.state.password}
placeholder='password'
onChange={this.handleChange}
/>
<Input type='submit' value='Log in' />
</Form>
</div>
</main>
)
}
}
Here the state is at the top of the form component. You could make it even more general and have a form Redux store with its own actions and reducers, but it should give you an idea.
Let me know if you have questions on the code.
Please use this.refs.email.value instead of this.refs.email.state.value.
Yes it is a standard way in react
OR
You can attach eventListener onClick and keep email in state.
export default class LoginPage extends React.Component {
constructor (props) {
super(props)
this.loginUser = this.loginUser.bind(this)
}
loginUser () {
console.log(`
Email: ${this.refs.email.value} Password: ${this.refs.password.value}
`)
}
changeEmail(event) {
this.setState({
email: event.target.value
}
render () {
return (
<main className={cx('LoginPage')}>
<div className={cx('container')}>
<Form onSubmit={this.loginUser}>
<Input type='email' value={this.state.value} onClick={this.changeEmail} placeholder='email' ref='email' />
<Input type='password' placeholder='password' ref='password' />
<Input type='submit' value='Log in' />
</Form>
</div>
</main>
)
}
}

Categories

Resources