Why changing state from false to true throws a Warning - javascript

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>

Related

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.

React Change Username

I am working on a task to practice react programming, this is the task - Change Username
Here is the explanation:
This application should allow the user to update their username by inputting a custom value and clicking the button.
The Username component is finished and should not be changed, but the App component is missing parts. Finish the App component so that the Username component displays the inputted text when the button is clicked.
The App component should use the React.useRef Hook to pass the input to the Username component for the input element and for the Username component.
For example, if the user inputs a new username of "John Doe" and clicks the button, the div element with id root should look like this:
<div><button>Change Username</button><input type="text"><h1>John Doe</h1></div>
This is the code given:
class Username extends React.Component {
state = { value: "" };
changeValue(value) {
this.setState({ value });
}
render() {
const { value } = this.state;
return <h1>{value}</h1>;
}
}
function App() {
function clickHandler() {}
return (
<div>
<button onClick={clickHandler}>Change Username</button>
<input type="text" />
<Username />
</div>
);
}
document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I tried a lot to understand how to solve this, but I am not able to fix this, how to solve this problem?
Just found it out. Seems like you need to use refs for everything. No state or anything allowed! Please note that you should not do it like that in a real world app :)
function App() {
const ref = React.useRef()
const inputRef = React.useRef()
function clickHandler() {
ref.current.changeValue(inputRef.current.value)
}
return (
<div>
<button onClick={clickHandler}>Change Username</button>
<input type="text" ref={inputRef} />
<Username ref={ref} />
</div>
);
}
This works
import React from 'react'
import ReactDOM from 'react-dom'
class Username extends React.Component {
constructor(props){
super(props);
this.state = {value: ""};
}
changeValue(value) {
this.setState({value: value});
}
render() {
const value = this.state.value;
return <h1>{value}</h1>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.userNameRef = React.createRef();
}
clickHandler() {
var name = document.getElementById('name_input').value;
this.userNameRef.current.setState({value: name});
}
render() {
return (
<div>
<button onClick={this.clickHandler.bind(this)}>Change Username</button>
<input id="name_input" type="text" />
<Username ref={this.userNameRef} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Make sure you understand the code and don't be like me, forgetting the bind method :-)
In the real world you would probably do something like this: -
import React, { useRef } from "react";
import ReactDOM from "react-dom";
class Username extends React.Component {
state = { value: "" };
changeValue(value) {
this.setState({ value });
}
render() {
const { value } = this.state;
return <h1>{value}</h1>;
}
}
function App() {
const myRef = useRef();
function clickHandler() {
document.querySelector("h1").innerHTML = myRef.current.value;
}
return (
<div>
<button onClick={clickHandler}>Change Username</button>
<input type="text" ref={myRef} />
<Username />
</div>
);
}
document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
document.querySelector("input").value = "John Doe";
document.querySelector("button").click();
setTimeout(() => console.log(document.getElementById("root").innerHTML));
This component structure probably isn't your best bet. Typically you want to have a Class component at the top with functional components on the bottom, and call those functional components within the Class component.
So rendering <button> within App is just making things hard for you. In App you should just be rendering <Username /> and have <Username /> holding your logic:
class Username extends Component {
constructor(props){
this.state = { usernameValue: ''};
this.onInputChange = this.onInputChange.bind(this);
this.changeUsername = this.changeUsername.bind(this);
}
onInputChange(event) {
this.setState({usernameValue: event.target.value});
}
changeUsername() {
//Update username in the DB
db.record = this.state.usernameValue
}
render(){
return (
<div>//Wrapper div
<input onChange={this.onInputChange} value={this.state.usernameValue} />
<button onClick={this.changeUsername}>Change Username</button>
</div>
);
}
}
function App(){
return(
<Username />
);
}
I did this a different way than what you were trying as you were trying to update the username by clicking the button, which you can do, but it would be better to update the state as you input the username, then use the button click as a form submission or something along those lines.
A good resource for this is 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

My inputs start with weird values

I've created very simple form for user to sign in. Here is my code:
import React,{ Component } from 'react';
class SignIn extends Component {
constructor(props) {
super(props);
this.state = {
login:"",
pass:""
}
}
signIn = (e) =>{
e.preventDefault();
alert("in")
}
handleChange = (propertyName) => (e) => {
const state = this.state;
const newState = {
...state,
[propertyName]: e.target.value
};
this.setState(newState);
}
render() {
return (
<div className="text-center">
<form onSubmit={this.signIn}>
<input type="text" id="login" onChange={this.handleChange('login')} value={this.state.login} placeholder="login"/>
<br />
<input type="password" id="pass" onChange={this.handleChange('pass')} value={this.state.pass} placeholder="pass"/>
<br />
<input type="submit" value="sign in" disabled={((this.state.login == "") && (this.state.pass == ""))
? true
: false}/>
</form>
</div>
);
}
}
export default SignIn;
For some reason every time I start my app these inputs already have some text inside. "login" always have "localhost" and "pass" contains some random numbers and letters. Can someone explain me where are these values comming from?
These fields are auto-populated by browser. See explanation here: https://developers.google.com/web/updates/2015/06/checkout-faster-with-autofill
Also you might be interested in this question of how people fighting with it :)
Chrome Browser Ignoring AutoComplete=Off

React.js Expected onSubmit listener to be a function, instead got type boolean

Can someone please tell me what the hell I am doing wrong here? I am pretty new to React, and everything I have read says that this should work. I am trying to call the function "addItem" when the form is submitted, however the console.log throws the error "Expected onSubmit listener to be a function, instead got type boolean" on page load. Thanks!
import React, { Component } from 'react';
import App from "./App"
import List from "./List"
class AddTodo extends Component{
constructor(){
super();
this.state = {
items: []
}
}
addItem(e){
var itemArray = this.state.items;
itemArray.push({
text: this._inputElement.value,
key: Date.now()
})
this.setState({
items: itemArray
})
this._inputElement.value = "";
console.log(itemArray)
e.preventDefault()
}
render(){
return(
<div className="box-wrapper">
<form onSubmit={this.addItem.bind(this)}>
<input href={(a) => this._inputElement = a} placeholder={this.props.placeHolder} type="text"></input>
<button type="submit">Submit</button>
</form>
<List entries={this.state.items} />
</div>
)
}
}
export default AddTodo;
Try changing your render and addItem to something like this:
addItem(e){
e.preventDefault();
{/* add the rest of the function here */}
}
render(){
return(
<div className="box-wrapper">
<form onSubmit={this.addItem.bind(this)}>
<input
href={(a) => this._inputElement = a}
placeholder={this.props.placeHolder}
type="text"
/>
<button type="submit" onClick={this.addItem.bind(this)}>Submit</button>
</form>
<List entries={this.state.items} />
</div>
)
}
I made two important changes:
Added e.preventDefault(); to the addItem method, which will prevent default behavior.
Added an onClick handler to the "submit" button, with the addItem method passed in as the target executable.
First have to remove event.preventDefault .It is restricting the default behaviour of the webpage.Add a onClick event listener to the submit so that the function executes on button click.
`import React, { Component } from 'react';
import App from "./App"
import List from "./List"
class AddTodo extends Component{
constructor(){
super();
this.state = {
items: []
}
}
addItem(e){
var itemArray = this.state.items;
itemArray.push({
text: this._inputElement.value,
key: Date.now()
})
this.setState({
items: itemArray
})
this._inputElement.value = "";
console.log(itemArray)
// e.preventDefault(); remove this prevent default
}
render(){
return(
<div className="box-wrapper">
<form onSubmit={this.addItem.bind(this)}>
<input href={(a) => this._inputElement = a} placeholder={this.props.placeHolder} type="text"></input>
<button type="submit" onClick={this.addItem.bind(this)}>Submit</button>
</form>
<List entries={this.state.items} />
</div>
)
}
}
export default AddTodo;`
I am not getting any error. But you should put the preventDefault at the top of the addItem, this will prevent from page reload. There is better way to handle the input value by assigning ref attribute to input and accessing via this.refs.refName.
addItem(e) {
e.preventDefault(); //To prevent the page reload on submit
var itemArray = this.state.items;
itemArray.push({
text: this.refs.inputElement.value
});
this.refs.inputElement.value = ""; // clearing the value
}
render() {
return(
<div>
<form onSubmit={this.addItem.bind(this)} >
<input ref="inputElement" placeholder={this.props.placeHolder} type="text"/>
<button type="submit">Submit</button>
</form>
{
this.state.itemArray.map((item) => {
return(<span>{item}</span>)
})
}
</div>
);
}

Categories

Resources