Keep getting a controlled vs uncontrolled react error - javascript

I keep getting this error in my console:
warning.js?8a56:36 Warning: LoginForm is changing a controlled input
of type password to be uncontrolled. Input elements should not switch
from controlled to uncontrolled (or vice versa). Decide between using
a controlled or uncontrolled input element for the lifetime of the
component. More info: https://facebook.github.io/react/docs/forms.html#controlled-components
I have had a look at these questions:
How to create a controlled input with empty default in React 15 - I define my state already
React Controlled vs Uncontrolled inputs - My function takes an event and not a password parameter
React - changing an uncontrolled input - I already define the password as empty
Here is the component:
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
values: {
username: "",
password: ""
}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this._clearInput = this._clearInput.bind(this);
}
onSubmit(event) {
// this.props.attemptLogin
event.preventDefault();
console.log(this.state.values);
let {username, password} = this.state.values;
this.props.attemptLogin(username, password)
.then((userInfo) => {
console.log(userInfo);
LocalStore.setJson('api', userInfo.api);
LocalStore.setJson('user', userInfo.user);
this._clearInput('username' , 'password');
})
.catch((err) => {
console.log("Failed:");
console.log(err);
this._clearInput('password');
});
}
_clearInput(...fields) {
let newValuesState = {};
fields.forEach((field) => {
newValuesState[field] = '';
});
this.setState({values: newValuesState});
}
onChange(event) {
let name = event.target.name;
let value = event.target.value;
this.setState({values: {[name]: value}});
}
render() {
return (
<div>
<form method="post" onSubmit={this.onSubmit}>
<div><input type="text" name="username" onChange={this.onChange} value={this.state.values.username}/></div>
<div><input type="password" name="password" onChange={this.onChange} value={this.state.values.password}/></div>
<div><button type="submit">Login</button></div>
</form>
</div>
);
}
}
LoginForm.propTypes = {
attemptLogin: React.PropTypes.func.isRequired
};
The code does seem to work but this error pops up in the console and so I can't continue I till I get it to stop appearing. Can anyone see what's I did wrong in the component?

Since you are nesting the values in state.values, your onChange function will remove the field not being changed. You could do something like this instead:
onChange(event) {
let name = event.target.name;
let value = event.target.value;
let values = {...this.state.values};
values[name] = value;
this.setState({values}});
}

Related

React setState doesn't work as I expected

I making a form validation on the page. In one of the tutorials I found a validation method that works correctly but I don't understand one thing.
I declare a variable before the class that stores the object with the default input values. When the field is wrong, the corresponding property is modified, while after correctly filling the form the fields are cleared to default values (errors properties becomes empty).
I don't understand the moment when state is set to the default values. Since the initialState object is a reference type, it should have different values after their change in the validate method.
For example, when I assign a string to the usernameError property, I expected this value to remain in this object. Meanwhile, when I do it:
this.setState(initialState);
Form errors will be cleared.
Shortened code:
const initialState = {
username: "",
surname: "",
email: "",
usernameError: "",
surnameError: "",
emailError: "",
};
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
this.handleSubmit = this.handleSubmit.bind(this);
}
validate() {
let usernameError = "";
if (!this.state.username) {
usernameError = "Fill in the missing field";
}
if ( usernameError ) {
this.setState({ usernameError });
return false;
}
return true;
}
handleSubmit(e) {
e.preventDefault();
const isValid = this.validate();
if (isValid) {
this.setState(initialState);
}
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
});
};
render() {
// contact form...
}
What you see is the correct behavior. You have to think of this.state = initialState as the new copy creation of initialState object. Because if its just the reference to initialState object, react state object will have no power to deal with state properties. So, to make your code working as per expectations, you have to make these objects separate, or create a copy explicitly. To reset errors, update the state as this.setState({usernameError :''});
Or in the other case of initialState the result will be same but you will be confused.

React, Redux Form: Change handler does not allow flipping of passed arguments in any manner

I encountered a quite strange issue recently. I have a class component in react which uses redux form. onChange event handler is called in case the input checkbox is toggled. Two arguments value and type(name of input) is passed to the event handler. When I console log the value it is properly showing the previous value. However, I cannot flip this boolean value in any manner i.e. the expression !value or let value = value?false:true always returns true/false depending on the initial state irrespective of the current state. I searched on the google & SO to find the cause but unable to find the same. Any kind of help is deeply appreciated.
Here is the code for your reference
class Example extends Component {
constructor(props){
super(props);
this.state = {
name: true
};
this.handleChange = this.handleChange.bind(this);
};
handleChange(value, type) {
console.log(value); //outputs previous value (before toggle)
let currValue = value ? false:true; //this line is not working
console.log(currValue); //outputs the same value (irrespective of state)
}
render() {
return (
<div>
<Field component="input" type="checkbox" name="name" id="name"
onChange={({target}) => this.handleChange(target.value,target.name)}/> Name
</div>
);
}
}
function mapStateToProps(state) {
return state;
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({change},dispatch);
}
export default compose(
reduxForm({
form: 'example', key: 'example',
initialValues: {
name: true
},
enableReinitialize: true
}),
connect(mapStateToProps, mapDispatchToProps)
)(Example);
Output (toggled twice: intial state checked)
You're not setting the value for Field. So, you'll need to set the value:
<Field
component="input"
type="checkbox"
name="name"
id="name"
onChange={({target}) => this.handleChange(target.value,target.name)}
value={this.state.name || true}
/>
The handler:
handleChange(value, type) {
this.setState({[type]: !value})
}
because the type of value is string your flip statement not working as you want
since "false" is consider to be true value.
console.log(Boolean("false"));
let currValue = false;
if(value == "false")
currValue = true;
your function should look like this
handleChange(value, type) {
console.log(value);
let currValue = false;
if(value == "false")
currValue = true;
console.log(currValue);
}

Type text into a React input using javascript (Tampermonkey script)?

I'm trying to make a Tampermonkey script that'll automatically enter text into some form input fields.
Normally, you can do this with just:
myElement.value = "my new text"
Problem is, this form is using React, and I can't directly change the value, since it doesn't set the React state.. How can I enter my desired data into these React components in my Tampermonkey script?
The answer could be found there https://github.com/facebook/react/issues/11488#issuecomment-347775628
let input = someInput;
let lastValue = input.value;
input.value = 'new value';
let event = new Event('input', { bubbles: true });
// hack React15
event.simulated = true;
// hack React16 内部定义了descriptor拦截value,此处重置状态
let tracker = input._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
}
input.dispatchEvent(event);
React doesn't expose component instances, so they aren't reachable without tampering an application on initialization, if this is possible.
Input values should be changed like they would be with vanilla JavaScript, by emitting DOM events.
React provides utility library that has helper functions to do that.
Here's an example. An input:
<input id="input" value={this.state.name} onChange={e => this.setState({ name: e.target.value })} />
And user script that runs after React application initialization:
import { Simulate } from 'react-dom/test-utils';
const input = document.getElementById('input');
input.value = 'Foo';
Simulate.change(input);
There's some good answers here, but none that work with TextArea. Here's a generic method stolen from another SO post which handles more cases:
const inputTypes = [
window.HTMLInputElement,
window.HTMLSelectElement,
window.HTMLTextAreaElement,
];
export const triggerInputChange = (node, value = '') => {
// only process the change on elements we know have a value setter in their constructor
if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {
const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
const event = new Event('input', { bubbles: true });
setValue.call(node, value);
node.dispatchEvent(event);
}
};
class HelloWorld extends React.Component{
constructor(props){
super(props);
this.state = {firstname: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e){
this.setState({firstname: e.target.value})
}
render(){
return(<div><input id="firstname" type=text onChange={(e) =>this.handleChange()} value={this.state.firstname} ></div>)
}
}

React.js - can't update form that is based on state

I'm trying to do a basic thing: insert data, which gets passed back to the main component, and display a list of items, that can be clicked and edited in the form.
Here's the relevant part of the code:
class SimpleForm extends React.Component {
constructor() {
super();
this.state = {
id: null,
firstName: ''
};
}
static getDerivedStateFromProps(props, state) {
if (props.user === null) return null;
return {
id: props.user.id,
firstName: props.user.firstName
}
}
handleChange = e => {
const value = e.target.value;
this.setState(prevState => {
return {
firstName: value
}
});
}
handleSubmit = e => {
e.preventDefault();
const event = e.target;
this.props.onAdd(this.state);
this.setState(prevState => {
return {
id: null,
firstName: ''
};
}, () => {
event.reset();
});
}
render() {
const {
firstName
} = this.state;
return ( <form onSubmit = {this.handleSubmit}>
<input type = "text"
name = "firstName"
value = {firstName}
onChange = {this.handleChange} />
<input type = "submit" value = "Submit" />
</form>
);
}
}
Here's the example: http://jsbin.com/yawasoz/1/edit?html,js,output
If you insert one item and then click on the "LI" element, you'll see that the state in the form gets set properly. However, you can't edit the data at all in the input - when I'm typing, the text stays the same. Much like if the "onChange" method didn't exist. What's going on here? I think that I might be using "getDerivedStateFromProps" incorrectly?
Apparently, this is a bug (as stated here https://www.reddit.com/r/reactjs/comments/8mslha/cant_update_form_that_is_based_on_state/dzq4zen) in the latest version of React, downgrading to 16.3 works as intended: http://jsbin.com/wilahihuyu/1/edit
<script crossorigin src="https://unpkg.com/react#16.3/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16.3/umd/react-dom.development.js"></script>
EDIT
Based on the discussion on github, this is NOT a bug, the bug was in the code and the version 16.4 made it apparent. Here's the discussion: https://github.com/facebook/react/issues/12898#issuecomment-392035163

Double setState method in one function

I am trying to create a autocomplete component. It's an input where user types the countru name and if letters match name of some country, the hints are displayed.
In my App Component i have method handleChange Within this method i change my state two times, which is bad idea.
How can I split it to change state in distinct methods ?
import React, { Component } from 'react';
import AutoComplete from './autoComplete.jsx';
import data from './data.json';
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
resoults: []
}
}
handleChange() {
let inputValue = this.refs.input.value;
this.setState({
inputValue: inputValue
});
let regular = "^" + this.state.inputValue;
let reg = new RegExp(regular , "i");
let filtered = data.filter((i,index)=> {
return (reg.test(i.name)
);
});
console.log(filtered);
this.setState({resoults:filtered})
}
render() {
return (
<div>
<input onChange={this.handleChange.bind(this)} type="text" ref="input"/>
<h3>You typed: {this.state.inputValue}</h3>
<AutoComplete resoults={this.state.resoults} />
</div>
);
}
}
export default App;
import React, {Component} from 'react';
class AutoComplete extends Component {
render() {
return (
<div>
<h4>autocompleteComponent</h4>
{this.props.resoults.map((i)=> {
return (
<ul>
<li>{i.name}</li>
</ul>
);
})}
</div>
);
}
}
export default AutoComplete;
I found myself in this position many times, but I got to the conclusion that it's better to compute the autocomplete options (in your case) without having them in the state of your component.
As I have used them until now, the state and props of a component should represent minimal data needed to render that specific component. Since you have your input value in the state, having the autocomplete options there also seems redundant to me. So here is what I propose:
class App extends Component {
this.state = {
inputValue: '',
};
handleChange(e) {
const inputValue = e.target.value;
this.setState({
inputValue,
});
}
computeResults() {
const {inputValue} = this.state;
// your functionality for computing results here
}
render() {
const {inputValue} = this.state;
const results = this.computeResults();
return (
<div>
<input type="text" onChange={this.handleChange.bind(this)} value={inputValue} />
<h2>You typed: {inputValue}</h2>
<Autocomplete results={results} />
</div>
);
}
}
Notes
Since your results come synchronously, via the .json import, this seems the perfect solution to me. If you want to get them via fetch or anything else, then you'll have to figure out a slightly different approach, but keep in mind that the state of your component should not contain redundant data.
Stop using ref with string value! and use refs when there is absolutely no other way because a React component should not generally deal with DOM operations directly. If you really need to use refs, use ref callbacks.
Hope this helps!
Use another function and setState callBack:
handleChange() {
let inputValue = this.refs.input.value;
this.setState(
{
inputValue: inputValue
},
() => this.secondFunc()
);
}
secondFunc() {
let regular = '^' + this.state.inputValue;
let reg = new RegExp(regular, 'i');
let filtered = data.filter((i, index) => {
return reg.test(i.name);
});
console.log(filtered);
this.setState({ resoults: filtered });
}

Categories

Resources