After changing username and password in the inputs, the properties of my state become 'undefined'. I just can't get my head around it: for some reason this.setState doesn't seem to work.
This is the code:
import * as React from 'react';
import { Link, RouteComponentProps } from 'react-router-dom';
import { connect } from 'react-redux';
import { ApplicationState } from '../store';
import * as LogonStore from '../store/LogonStore';
type LogonProps =
LogonStore.LogonState
& typeof LogonStore.actionCreators
& RouteComponentProps<{}>;
class Logon extends React.Component<any, any> {
public constructor(props) {
super(props);
this.state = { un: '', pw: '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
console.log(event.target.value); // works fine
this.setState({ un: event.target.un, pw: event.target.pw });
console.log(this.state); //first un: '', pw: '' then un: undefined, pw: undefined
}
public render() {
return <div >
<h1>Logon</h1>
<label>username</label><input value={this.state.un} onChange={this.handleChange} />
<label>password</label><input value={this.state.pw} onChange={this.handleChange} />
<br />
</div>;
}
}
export default connect(
(state: ApplicationState) => state.logon, // Selects which state properties are merged into the component's props
LogonStore.actionCreators // Selects which action creators are merged into the component's props
)(Logon) as typeof Logon;
I'm missing something fundamental here...
Thanks!
You can only handle the change of one input at a time. Here is the problem code:
this.setState({ un: event.target.un, pw: event.target.pw });
When you type in a form field, your handleChange function will get called. The event.target is the HTML input element of the thing you just changed... meaning you cannot update the values for both username and password at the same time like you're trying to do. You can only update values one at a time. In order to facilitate this, you will need to add a "name" property to your input elements:
<input name="un" value={this.state.un} onChange={this.handleChange} />
<input name="pw" value={this.state.pw} onChange={this.handleChange} />
Then update your state like this:
this.setState({ [event.target.name]: event.target.value });
class App extends React.Component {
constructor(props) {
super(props);
this.state={
un:'',
pw:''
}
}
handleChange=(event)=> {
this.setState({[event.target.name]: event.target.value},
() => {
//updated state
console.log(this.state)
});
}
render() {
return <div>
<h1> Logon </h1>
<label> username </label>
<input name="un" value={this.state.un} onChange={this.handleChange}/>
<br />
<label> password </label>
<input name="pw" value={this.state.pw} onChange={this.handleChange}/>
</div>
}
}
ReactDOM.render( <App/> , document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Explanation as asked by OP:
setState is asynchronous.
It takes time to mutate the changes. So instead of checking state on next immediate line, you can log or check it as shown in the answer.
For more detailed information on this please refer setState()
setState is an asynchronous function. Thus, you cannot access it on the next line, but I assure you it's working. Also, if you really want to log the values from state you can either put the log inside the render method OR use the setState callback, which is the second argument of the setState function, like so:
this.setState(
{ un: event.target.value.un, pw: event.target.value.pw },
() => console.log(this.state)
);
Related
I am trying to have a controlled input set up in a child component (the Search component). I wanted to keep the input state in the main App component so that I can access it in my apiCall method. I am getting the following error:
Warning: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.
However, I did add an onChange handler. I'm assuming the problem is that the onChange handler function is in the parent component and React doesn't like this. I did try moving the input to the main App component and worked fine (logged input to console).
Am I going about this wrong? And is there a way to set it up so that I can access the input from the Search component in the App component? I was hoping to keep most of my code/functions/state in the main App component.
Here is the App component
import React, { Component } from "react";
import './App.css';
import Header from './Components/Header'
import Search from './Components/Search'
import MainInfo from './Components/MainInfo'
import Details from './Components/Details'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
weather: null,
main: '',
wind: '',
loading: null,
cityInput: 'Houston',
city: 'City Name',
date: new Date()
};
this.apiCall = this.apiCall.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
cityInput: event.target.value
})
console.log(this.state.cityInput)
}
// Fetch data from OpenWeatherAPI
apiCall() {
this.setState({
loading: true
})
const currentWeather = fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${this.state.cityInput}&appid={apiKey}&units=imperial`
).then((res) => res.json());
const futureWeather = fetch(
`https://api.openweathermap.org/data/2.5/forecast?q=houston&appid={apiKey}&units=imperial`
).then((res) => res.json());
const allData = Promise.all([currentWeather, futureWeather]);
// attach then() handler to the allData Promise
allData.then((res) => {
this.setState({
weather: res[0].weather,
main: res[0].main,
wind: res[0].wind,
city: res[0].name
})
});
}
componentDidMount() {
this.apiCall();
}
render() {
return (
<div className="container-fluid bg-primary vh-100 vw-100 d-flex flex-column align-items-center justify-content-around p-3">
<Header />
<Search cityInput={this.state.cityInput} />
<MainInfo main={this.state.main} date={this.state.date} city={this.state.city} weather={this.state.weather} />
<Details main={this.state.main} wind={this.state.wind} />
</div>
);
}
}
export default App;
Here is Search component
import React, { Component } from "react";
class Search extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="row">
<div className="col-12">
<div className="d-flex">
<input className="form-control shadow-none mx-1" placeholder="Enter a city..." value={this.props.cityInput} onChange={this.handleChange}></input>
<button className="btn btn-light shadow-none mx-1" onClick={this.apiCall}>Test</button></div>
</div>
</div>
);
}
}
export default Search;
The Search component is indeed unaware of the implementation of the onChange function you have made in your App. If you really want to use a function from the parent (App) component in the child (Search), you'll need to add it as a property, as such:
<Search cityInput={this.state.cityInput} onChange={this.onChange} />
Then, you need to set it in the Child component's constructor:
constructor(props) {
super(props);
this.onChange = props.onChange;
}
I also suggest you'll have a look at React's functional approach with hooks https://reactjs.org/docs/hooks-intro.html, which makes all this a whole lot less fiddly, in my opinion. But it might take a bit to get used to.
u can pass functions like ur handler over the prop to childrens and update so from a child to the mother of the children, in the children u give the value the prop u supply from mother
<Select dataFeld="broker" id="yourid" value={this.state.brokerSel} ownonChange={(e) => this.setState({statename: e})
I understand that this question has been asked many different times. I've looked through many different ones and I'm still having a difficult time understanding it.
I've read many articles online and I know that I need to pass my state to props and I'm having a difficult time doing so. I managed to pass a simple string of test, however I cannot pass my state to props as it simply returns nothing and I'm not sure why if the values on the form get updated in the onchange method.
I also want to avoid using redux as a alternative as I'm trying to learn the basic way first
What I'm trying to do is very simple, user fills out a box that contains ordernumber on the form. They hit submit, redirects to another page where I'll have access to the ordernum they submitted on the input box for the order number.
Here is my code:
simple input form page
/*eslint-disable no-unused-vars*/
import React from 'react';
import ReactDOM from 'react-dom';
class Reloform extends React.Component {
constructor(props){
super(props);
this.state = {
orderNum: "",
errorMsg: ""
}
}
onChange(e){
this.setState({
[e.target.name]: e.target.value
})
}
onSubmit(e){
if(this.state.orderNum === '') {
this.setState({
errorMsg: 'Please enter your order number.'
});
} else {
this.setState({
errorMsg: ''
});
// Submission successful
window.location = '/relotoForm';
}
e.preventDefault();
}
render() {
console.log(this.props)
return (
<div className="container">
// I get message prop back but not orderNum?
<h1>{this.props.message}</h1>
<h1>{this.props.orderNum}</h1>
<div className="reloContainer">
<form
method="POST"
id="reloForm"
onSubmit={e => this.onSubmit(e)}
autoComplete="off"
>
<h1>T/O Form</h1>
{this.state.errorMsg !== '' ? <p style={{color:'#E2231A'}}>Please enter an order number.</p> : ''}
<label>Order #</label>
<input
type="text"
name="orderNum"
className="form-control"
value={this.state.orderNum}
onChange={e => this.onChange(e)}
/>
<button type="submit" className="btn btn primary" id="reloButton" onClick={this.props.updateData}>Submit</button>
</form>
</div>
</div>
)
}
}
export default Reloform;
/* form action page */
/*eslint-disable no-unused-vars*/
import React from 'react';
import ReactDOM from 'react-dom';
import Reloform from './reloForm';
class Relotoform extends React.Component {
render() {
return (
<div>
//The string that I returned works but the state that I try to have access on this page does not. I am not sure why. My assumption is because maybe this.state is referring to the state of this component and not the form component on my form page?
<Reloform message="Works" orderNum={this.state.orderNum} />
</div>
)
}
}
export default Relotoform;
State is local to the component you are accessing. If you want to pass a state value to another component, this is where you use props. So when you are trying to render Reloform in your Relotoform component, when you do orderNum={this.state.orderNum}, you won't get any value as there is no state defined for Relotoform in which there's a variable called orderNum.
You need to update the orderNum through your state in the Reloform. You get message displayed because you are passing the value "Works" as a prop in Relotoform. Then you are accessing it correctly as a prop in Reloform. Adapt a similar arhcitecture for orderNum.
From the react quickstart: https://facebook.github.io/react/docs/lifting-state-up.html
The purpose of the line:
handleChange(e) {
this.props.onChange(e.target.value);
}
was never explained and I can't figure out what it does. Is onChange a built in method of props? I thought props was simply just parameters for components, why can it invoke functions?
I came here with the same question, but I understand it now (at least I think I do). The issue is that onChange in the Calculator class is a prop, but in the render portion of the class TemperatureInput, it's an event listener. Sometimes I see this in React, where the same name is used on two completely different things, and I find it can easily create confusion. Here's what happens in the following code block:
class TemperatureInput extends React.Component {
handleChange(e) {
this.props.onChange(e.target.value); **(2)**
}
render() {
return (
<fieldset>
<input value={value} onChange={this.handleChange} /> **(3)**
</fieldset>
);
}
}
class Calculator extends React.Component {
handleCelsiusChange(value) {
this.setState({scale: 'c', value});
}
render() {
return (
<div>
<TemperatureInput
scale="c"
value={celsius}
onChange={this.handleCelsiusChange} /> **(1)**
</div>
);
}
}
In Calculator, a made up prop called onChange passes a reference to the method handleCelsiusChange to TemperatureInput
this.props.onChange() in TemperatureInput is now a reference to handleCelsiusChange() in Calculator
onChange={this.handleChange} is an event listener, which will fire handleChange() up on the change of the input field.
In conclusion, onChange as a prop is custom, onChange as an event listener is built in. The prop just passes a function reference from an ancestor to the child so you can run it in the child.
I thought props was simply just parameters for components, why can it invoke functions?
You're right, but those parameters can also be callbacks/functions. E.g:
Definition:
class Comp extends Component {
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
return (<input onChange={this.handleChange.bind(this)) />
}
}
Usage:
<Comp onChange={(a) => console.log(a)} />
Got a clue from SeattleFrey's answer, maybe the author of that code shouldn't name this parameter onchange. It is so confusing for a starter of ReactJs like me.
I name it myChange instead of onChange. It is actually a function passed in as a parameter. And e.target.value is the parameter for that function.Props can contain objects as well as functions, since functions are also objects in Javascript
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.myChange(e.target.value);
}
render() {
const value = this.props.value;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={value}
onChange={this.handleChange} />
</fieldset>
);
}
}
class Calculator extends React.Component {
handleCelsiusChange(value) {
this.setState({scale: 'c', value});
}
render() {
return (
<div>
<TemperatureInput
scale="c"
value={celsius}
myChange={this.handleCelsiusChange} />
</div>
);
}
}
You just missed this sentence:
If several components need access to the same state, it is a sign that the state should be lifted up to their closest common ancestor instead. In our case, this is the Calculator.
Your quoted code is from component TemperatureInput:
class TemperatureInput extends React.Component {
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
return (
<fieldset>
<input value={value} onChange={this.handleChange} />
</fieldset>
);
}
}
And TemperatureInput is a child component of Calculator, where TemperatureInput.onChange is assigned to Calculator. handleCelsiusChange
class Calculator extends React.Component {
handleCelsiusChange(value) {
this.setState({scale: 'c', value});
}
render() {
return (
<div>
<TemperatureInput
scale="c"
value={celsius}
onChange={this.handleCelsiusChange} />
</div>
);
}
}
I'm using webpack with React and I can't for the life of me understand what is going on in this build. This is what's supposed to be happening.
The var headerInput changes to whatever value is inputted onChange.
When the form is submitted(onSubmit) the console.log logs the headerInput value.
The problem: The value that gets console logged is numerical, it's usually something like: .0.0.1. I think it's console.log'ing the click event. Why isn't the value being assigned like in the in the handlerInput function?
Any help is much appreciated.
Thanks, All.
var headerInput = null;
import React from "react";
export default class Navigation extends React.Component{
handlerInput(e,headerInput){
headerInput = e.target.value;
console.log(headerInput);
};
clickSubmit(e,headerInput){
e.preventDefault();
console.log(headerInput);
};
render(){
return(
<form onSubmit={this.clickSubmit.bind(this)}>
<input type="text" placeholder="change header" onChange={this.handlerInput.bind(this)} />
<button>Change Header</button>
</form>
);
}
};
This is not the recommended way to be using React. Instead of relying on a "global" to store your state, you should be using the state API that come with components.
Like so:
import React from "react";
export default class Navigation extends React.Component{
constructor(props) {
super(props);
// Set up your default state here.
this.state = { };
// Do early binding.
this.handlerInput = this.handlerInput.bind(this);
this.clickSubmit = this.clickSubmit.bind(this);
}
handlerInput(e){
// Use setState to update the state.
this.setState({
headerInput: e.target.value
}
};
clickSubmit(e){
e.preventDefault();
// You read state via this.state
console.log(this.state.headerInput);
};
render(){
return(
<form onSubmit={this.clickSubmit}>
/* Make sure you pass the current state to the input */
<input
type="text"
placeholder="change header"
onChange={this.handlerInput}
value={this.state.headerInput}
/>
<button>Change Header</button>
</form>
);
}
};
I definitely recommend you revisit the official react docs, like the thinking in react and react forms tutorials.
If the input is strictly one-way (you only read from it) then just use a ref
import React from "react";
class Navigation extends React.Component{
clickSubmit(e,headerInput){
e.preventDefault();
console.log(this.inputEl.value);
};
render(){
return(
<form onSubmit={this.clickSubmit.bind(this)}>
<input placeholder="change header" ref={el => this.inputEl = el} />
<button>Change Header</button>
</form>
);
}
};
Note that...
Although string refs are not deprecated, they are considered legacy,
and will likely be deprecated at some point in the future. Callback
refs are preferred.
https://facebook.github.io/react/docs/more-about-refs.html
I am trying to build a form with custom input fields. Problem is structure of form is not pre known. And i want it to abstract as many common functionalities as i can. So i created a form component which will actually take care of storing value of all input components and action on submit. Rest inside form how html should be that will be written by the user.
here is a snapshot:
a custom input field:
class InputField extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
id: this.props.id,
value: this.props.value || '',
valid: this.props.isValidationRequired || this.props.required ? true : false,
errorVisible: false,
errorMessage: ''
};
}
//different helper and conchange function
render() {
let props = this._getInputFieldProps();
return (<div className={this.props.fieldParentClass}>
<label for={this.props.id}>{this.props.name}</label>
<input {...props}/>
<span className={this.state.errorVisible ? 'show' : 'hide'}>{this.state.errorMessage}</span>
</div>)
}
}
export default InputField;
form:-
class FormComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
submitButton: {
buttonClasses: ['btn-default', 'vy-btn-secondary'],
buttonText: "SUBMIT",
}
};
}
//functions for storing values of children and submit
render() {
return <form role="form" class="vm-form">
<Button disabled={this.state.submitValid} onClick={this._onSubmit.bind(this)} button={this.state.submitButton}></Button>
</form>
}
}
export default FormComponent;
and caller class:-
class LeadDetails extends React.Component {
constructor(HttpService, $state, VymoDataService, props, context) {
super(props, context);
}
render() {
return (<FormComponent >
<InputField placeholder='hi' value="1"
type="text"
id="name"
maxlength="10"></InputField>
</FormComponent>)
}
}
so now i can wrap my input field component in some html wrapper and customise the look the way i want. But here only form is getting render not input field, neither its behaving as child.
Have I got it totally wrong in some way. What should have been my approach?
In your FormComponent render method, you need to render the "children", which will be located on this.props.children:
render() {
return <form role="form" class="vm-form">
{this.props.children}
<Button disabled={this.state.submitValid} onClick={this._onSubmit.bind(this)} button={this.state.submitButton}></Button>
</form>
}
The reason for this is when you go to actually create (instantiate) the form, you are doing the following:
<FormComponent >
<InputField placeholder='hi' value="1"
type="text"
id="name"
maxlength="10"></InputField>
</FormComponent>
The stuff in between the component tags are the "children". The children are passed to your component during instantiation (props.children), it's up to the component to render those children. The reason you are probably expecting the children to be rendered automatically is because React treats <ReactComponents> differently from <normal-dom-elements>.
Whenever react sees a <Tag> which begins with an upper case letter, it assumes that it's a react component and basically converts this:
<Tag foo="bar"><div>hello</div></Tag>
to something like this:
new Tag({
foo: "bar",
children: React.createElement('div', null, "hello")
})