Ok, so I'm trying to build a form in React where I can enter values in multiple inputs, then submit and have the values populate designated cells in a table. I'm trying to get the state to update using 'onChange', but when I enter the values, my initial state gets overwritten entirely.
So, if I set:
state = {
Jan012019: {
first: null,
second: null
}
};
then try to update state by entering '3' into the input for 'first' using:
this.setState(
{
Jan012019: {
[e.target.name]: e.target.value
}
},
function() {
console.log(this.state);
}
);
state displays as:
Jan012019 {
first: '3'
}
completely removing 'second' from state, and if I try to then also enter values into the 'second' input, it removes 'first' from the state. What's going on here? I've seen other examples and solutions, and I'm fairly certain my code was exactly like a solution from another question on here, but still won't work correctly. Full code below.
import React from "react";
class InputForm extends React.Component {
state = {
Jan012019: {
first: null,
second: null
}
};
updateTable = e => {
this.setState(
{
Jan012019: {
[e.target.name]: e.target.value
}
},
function() {
console.log(this.state);
}
);
};
onClick = e => {
e.preventDefault();
console.log(this.state);
};
render() {
return (
<form className="ui form" style={{ marginTop: "50px" }}>
<div className="inline field">
<label style={{ marginRight: "27px" }}>First Input</label>
<input
name="first"
type="number"
placeholder="Enter value"
onChange={this.updateTable}
/>
</div>
<div className="inline field">
<label>Second Input</label>
<input
name="second"
type="number"
placeholder="Enter value"
onChange={this.updateTable}
/>
</div>
<button onClick={this.onClick}>Click</button>
</form>
);
}
}
export default InputForm;
I also tried setting the input values to:
value={this.state.Jan012019.first}
to see if that made any difference, but no go.
That is because you are resetting the whole Jan012019 object in your setState.
Jan012019: {
[e.target.name]: e.target.value
}
You need to spread the original Jan012019 object first, to preserve the other fields
this.setState({
Jan012019: {
...this.state.Jan012019, [e.target.name]: e.target.value
}
})
Related
I have a simple React component that I'm working on creating right now. Basically, the user can input an ID and when they submit, it will display some information that is in a container. The code looks like so
export default class IDContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
Id: '',
isSubmitted: false
};
}
handleSubmit = (event) => {
this.setState({
isSubmitted: true
});
};
handleChange = (event) => {
this.setState({
Id: event.target.value
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<Input type={'text'} placeholder={"Enter Id"} value={this.state.Id} onChange={this.handleChange} />
<Button type={'submit'} > Lookup </Button>
</div>
<div>
{this.state.isSubmitted && <DetailsContainer Id={this.state.Id} />}
</div>
</form>
);
}
}
The details container has already been created and just returns some details about the Id that has been passed in. I can show the details of the first Id that I pass in just fine. However, when I enter in another Id and submit the form, the DetailsContainer is not re-rendering and is still showing the details for the older Id. I tried moving it around and adding some logic (I even put the DetailsContainer in my state to see if I can manipulate it that way), but that doesn't seem to be working. I see that there is a shouldComponentUpdate() method, and that seems to be what I need to use, but the guides I saw all place it inside of the DetailsContainer. Anyway for me to have it in IDContainer, or is there an easier way to re-render the DetailsContainer?
I think part of the issue here is that once isSubmitted is set, every change you make to the input will be applied to this.state.Id and passed into DetailsContainer.
I think you'd be better off having one variable for tracking the input state, and variable one for tracking the Id you want to pass into DetailsContainer.
state = { Id: null, inputId: '' };
handleSubmit = (event) => {
this.setState({
Id: this.state.inputId
});
};
handleChange = (event) => {
this.setState({
inputId: event.target.value
});
};
render() {
return (
...
<Input ... value={this.state.inputId} />
...
{this.state.Id !== null ? <DetailsContainer Id={this.state.Id} /> : null}
);
}
I'm building a ReactJS search component for data filtering through search.
The idea is that the user types a word, letter after letter, and the system will filter all registers containing that word. The basic component is detailed below:
class SearchInput extends Component {
static propTypes = {
onKeyUp: PropTypes.func,
placeHolder: PropTypes.string,
value: PropTypes.string
};
state = {
searchText: ""
};
handleKeyUp = event => {
console.log(event.target.value) // <== No result. Always empty
let newSearchText = event.target.value;
this.setState({ searchText: newSearchText });
if (this.props.onKeyUp) this.props.onKeyUp(newSearchText);
};
render() {
console.log(this.state.searchText) // <== Always empty
return (
<div className="search-input">
<div className="search-input-icon">
<Icon name="faSearch" />
</div>
<input
autoFocus="true"
type="text"
onKeyUp={this.handleKeyUp}
placeholder={this.props.placeHolder}
value={this.state.searchText}
/>
</div>
);
}
I'm not getting the key pressed value on the handleKeyUp event handler.
It works if I ommit the value={this.state.searchText} (uncontrolled) from the code, but I need a way to set the searchText from outside the component (initialization, other component selection, etc.).
Why am I not getting the event.target.value data on my handler? How to fix it?
I'm pretty sure you have to listen to the onChange event on an input field to get the updated target value. simply change
<input onKeyUp={this.handleKeyUp} />
to
<input onChange={this.handleKeyUp} />
Try to use event.key instead.
The event.target.value just points to your this.state.searchText which hasn't been set yet.
seems you forgot to bind the function on the constructor:
class SearchInput extends Component {
constructor(props) {
super(props);
this.handleKeyUp = this.handleKeyUp.bind(this);
}
//... any code here
handleKeyUp = event => {
console.log(event.target.value);
}
render() {
//... any code here
<input
autoFocus="true"
type="text"
onKeyUp={this.handleKeyUp}
placeholder={this.props.placeHolder}
value={this.state.searchText}
/>
}
}
Use this:
let newSearchText = event.target.getAttribute('value')
I working on a small personal project using React on Rails. I am very new to both of these things.
I have a react component that is a form. I also have another component that has some inputs that the user can add as many as needed. Using the Add Properties button. I am trying to save the state of each input that is added. I could have the component itself save the state but how then would I send it with my fetch post request that happens onClick?
I have looked at react's context API but cant figure out if this would help me. Also I have never used redux so it is possible I should look into that as well.
https://reactjs.org/docs/context.html
https://redux.js.org/basics/usagewithreact
I understand that I don't want to reach into state. So I was trying to figure out how to create an array of objects that will hold the input values of each input pair. But I cannot seem to wrap my mind around how to implement.
class ProductsCreate extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
upc: '',
availableOn: '',
inputs: []
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (e.target.name === 'name') {
this.setState({ name: e.target.value });
}
if (e.target.name === 'upc') {
this.setState({ upc: e.target.value });
}
if (e.target.name === 'date') {
this.setState({ availableOn: e.target.value });
}
}
submitData = () => {
fetch(`/send_data`, {
method: 'POST',
body: JSON.stringify({
name: this.state.name,
upc: this.state.upc,
availableOn: this.state.availableOn
}),
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
})
.then(response => {
return response.json;
})
.then(data => {
console.log(data);
});
};
clickHandler = e => {
e.preventDefault();
this.submitData();
};
appendInput = e => {
e.preventDefault();
const newInput = `input-${this.state.inputs.length}`;
this.setState({ inputs: this.state.inputs.concat([newInput]) });
};
render() {
return (
<div className="form_container">
<h1>Products</h1>
<form>
<label>Name</label>
<input type="text" name="name" onChange={this.handleChange} />
<label>UPC</label>
<input type="text" name="upc" onChange={this.handleChange} />
<label>Availiable On</label>
<input
type="text"
name="date"
placeholder="mm/dd/yyyy"
onChange={this.handleChange}
/>
<h1>Properties</h1>
{this.state.inputs.map(input => (
<Properties key={input} />
))}
<button onClick={this.appendInput}>Add Properties</button>
<button onClick={this.clickHandler}>Save</button>
</form>
</div>
);
}
}
export default ProductsCreate;
This is the component that will be added on click
import React from 'react';
class Properties extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="property_container">
<label>Property Name</label>
<input type="text" name="propertyName" />
<label>Property Value</label>
<input type="text" name="propertyValue" />
</div>
);
}
}
export default Properties;
Pass handle change as prop to Properties component and then use that prop in input onChange within your Properties component. Make the following edits. I also suggest if you don't want to continously update the state on every character typed use debounce.
ProductsCreate
{this.state.inputs.map(input => (
<Properties key={input} onChange={this.handleChange} name={input}/>
))}
Properties
<input type="text" name={this.props.name} onChange={this.props.onChange}/>
I recently got started with React and want to build a little application to fetch weather data. My API has a function to return autocomplete suggestions. So when my autosuggestion array is not empty I render a list and upon clicking one of the <li>'s I want the value inside of the input box. I manage to set the state of my SearchBar but can't change it's value.
Edit: I try to get my value from changeState() into my <input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} />. I can search for terms otherwise.
import React from 'react';
import './SearchBar.css';
import Suggestion from './Suggestion';
class SearchBar extends React.Component{
constructor(props) {
super(props);
this.state = {inputValue: ''};
this.search = this.search.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.changeState = this.changeState.bind(this);
}
changeState(value) {
console.log(value);
// Logs value of text between <li></li>
this.setState({inputValue: value});
}
search() {
this.props.onSearch(this.state.inputValue);
}
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
});
this.props.onChange(this.state.inputValue);
}
handleKeyPress(e) {
if(e.key === 'Enter') {
this.search();
}
}
render() {
return (
<div>
<div className="SearchGroup" onKeyPress={this.handleKeyPress} >
<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} />
<a onClick={this.search}>Go</a>
</div>
<Suggestion autocomplete={this.props.autocomplete} onSelect={this.changeState} />
</div>
);
}
}
export default SearchBar;
For the sake of completeness my Suggestion.js:
import React from 'react';
import './Suggestion.css';
class Suggestion extends React.Component{
constructor(props) {
super(props);
this.updateInputField = this.updateInputField.bind(this);
}
updateInputField(evt) {
this.props.onSelect(evt.currentTarget.innerText);
}
render(){
if(this.props.autocomplete && this.props.autocomplete.length > 0) {
return (
<div className="Suggestion">
<ul>
{
this.props.autocomplete.map((location) => {
return (
<li key={location.id} onClick={this.updateInputField}>{location.name}</li>
)
})
}
</ul>
</div>
);
} else {
return <div className="None"></div>
}
}
}
export default Suggestion;
I would also prefer to submit location.url in Suggestion, but I could not find a property that matches inside of evt.
As mentioned in my comment. You are setting state and immediately passing state to onChange function in updateInputValue event handler function which is not correct. Because you won't get the state value updated immediately, the state value updates only when it renders so, pass evt.target.value directly like below
updateInputValue(evt) {
this.setState({ inputValue: evt.target.value });
this.props.onChange(evt.target.value);
}
In order to see chnaged value on your input field, you have to pass value prop to input tag like below
<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} value={this.state.inputValue}/>
I would guess that you are trying to use value from state that isnt there yet, because setState is asynchronous
so either use callback on setState
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
}, ()=> this.props.onChange(this.state.inputValue));
}
or, use the value from event directly
updateInputValue(evt) {
const value = evt.target.value
this.setState({
inputValue: value
});
this.props.onChange(value)
}
plus you havent assigned value back to your input:
<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} value={this.state.inputValue}/>
The React setState doesn't update the state immediately. It puts it in the queue and updates the state in batches. if you want to access the updated state write the code in the setState callBack
this.setState({ inputValue: evt.target.value},()=> this.props.onChange(this.state.inputValue));
something like this
This question already has answers here:
How to disable button in React.js
(8 answers)
Closed 3 years ago.
I am using trying to disable a button in react based on couple states. Down below is a breakdown of my code
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
disabled: true
}
this.handleChange = this.handleChange.bind(this);
this.handlePass = this.handlePass.bind(this);
}
pretty self explanatory constructor. The disabled will be changed as state changes. My render method looks something like this
render() {
if(this.state.email && this.state.pass) {
this.setState({ disabled: false })
}
return (
<div className='container'>
<div className='top'></div>
<div className='card'>
<MuiThemeProvider>
<Card >
<div className='wrapper'>
<TextField
hintText="Email"
value={this.state.email} onChange={this.handleChange}
/><br/>
<TextField
hintText="Password"
type="password"
/><br/>
<div className='login-btn'>
<RaisedButton label="Login" primary={true}
disabled={this.state.disabled} />
</div>
</div>
</Card>
</MuiThemeProvider>
</div>
</div>
)
}
As you can see I have 2 text fields and I am handeling the data changes with the following method
handleChange(e) {
this.setState({email: e.target.value});
}
handlePass(e) {
this.setState({pass: e.target.value});
}
Now my button is initially disabled and everytime a state is changed and component re-renders I want to check for state changes and enable button accordingly. So I was thinking of using the life cycle method like so
componentWillMount() {
if(this.state.pass && this.state.disabled) {
this.setState({disabled: false})
}
}
However, this doesn't work. When both email and password field is not empty the button stays disabled. I am not sure what am I doing wrong.
Please, do not set states inside render() function. That might cause infinite loops to occur.
Refer: https://github.com/facebook/react/issues/5591
Instead of setting states inside render() function, you can set the disabled state inside the handleChange() and handlePass() function.
If more detail required, please do mention.
You should be setting the disabled state inside your handleChange and handlePass functions.
componentWillMount() only runs right before the component is rendered, but never again.
Just made a demo , is that you need, check the code in the demo below
demo
Change below code :
class App extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
invalidData: true
}
this.onEmailChange = this.onEmailChange.bind(this);
this.onPasswordChange = this.onPasswordChange.bind(this);
}
// componentWillUpdate is to be deprecated
//componentWillUpdate(nextProps, nextState) {
// nextState.invalidData = !(nextState.email && nextState.pass);
//}
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onPasswordChange(event) {
this.setState({ pass: event.target.value });
}
render() {
return (
<form>
<input value={this.state.email} onChange={this.onEmailChange} placeholder="Email" />
<input value={this.state.password} onChange={this.onPasswordChange} placeholder="Password" />
// from this <button disabled={this.state.invalidData}>Submit</button>
//to
<button disabled={!(this.state.email && this.state.password)}>Submit</button>
</form>
);
}
}
**updated **
disable submit button in <button disabled={!(this.state.email && this.state.password)}>Submit</button> itself.