I'm trying to use setState to update a state array in React using a basic HTML input.
The state I'm trying to update looks like:
"businessName": "",
"grossAnnualSales": 0,
"contactEmail": "",
"numEmployees": 0,
"annualPayroll": 0,
"locations": [{"zip": ""}],
"industryId": "",
I need to add a zip code inputted by the user in a React component to this object in the array.
So far I've tried this and it's not working, it just updates to a string and not an array:
updateZip(){
return e => this.setState({"locations": [{"zip": e.currentTarget.value}]})
}
The React component:
<input onChange={this.updateZip()} type="text" className="zip-input"/>
How can I update the state succesfully?
Try replacing your updateZip function with an arrow function.
updateZip = (e) => {
return e => this.setState({"locations": [{"zip": e.currentTarget.value}]}) }
Also
<input onChange={(e) => this.updateZip(e)} type="text" className="zip-input"/>
use e.target.value and pass onChange={this.updateZip}
class App extends Component {
state = {
locations: [{ zip: "" }]
};
updateZip = (e) => {
this.setState({ locations: [{ zip: e.target.value }] });
};
render() {
return (
<div>
<input onChange={this.updateZip} type="text" className="zip-input" />
<p>{this.state.locations[0].zip}</p>
</div>
);
}
}
export default App;
CodeSandBox
Related
I'm new to React and I'm trying create a To Do List project.
I'm trying to add a new task to my tasks's array via input, but when I press Enter nothing is added to screen. Can someone help?
App.js
import React, { Component } from "react";
import Tasks from "./Components/tasks";
class App extends Component {
constructor(props) {
super(props);
this.state = {
newTask: '',
tasks: [
{ id: 1, text: "study" },
{ id: 2, text: "read" },
{ id: 3, text: "gym" },
]
};
}
handleSubmit(e) {
e.preventDefault();
const tasks = [...this.state.tasks];
tasks.push({id: 4, text: this.state.newTask});
this.setState({ tasks: tasks });
}
handleChange= (e) => {
this.setState({newTask: e.target.value});
}
render() {
return (
<div className="App">
<h1>To Do List</h1>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Enter task" value={this.state.newTask} onChange={this.handleChange}/>
</form>
<Tasks tasks={this.state.tasks} />
</div>
);
}
}
export default App;
Adicionaly I'm getting this error on the console:
error
you need to bind your function to the class
simple solution is to use arrow function syntax
handleSubmit = (e) => {
instead of
handleSubmit(e) {
there are other ways to do it as well..
you can read this article to understand more https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/
Getting this error when trying to add input from the web-application to the database. Struggeling to understad what the problem is. The error come when the button to insert to the database is used. Here's some of the code:
import React, { Component } from 'react';
import ProductService from './ProductService';
class Input extends Component {
constructor(props) {
super(props);
this.onChangeName = this.onChangeName.bind(this);
this.onChangeQuantity = this.onChangeQuantity.bind(this);
this.onChangePrice = this.onChangePrice.bind(this);
this.state = {
id: -1,
name: null,
quantity: null,
price: null
};
}
onChangeName(e) {
this.setState({
name: e.target.value
});
}
onChangeQuantity(e) {
this.setState({
quantity: e.target.value
});
}
onChangePrice(e) {
this.setState({
price: e.target.value
});
}
saveProduct() {
var data = {
name: this.state.name, //this is the line witch get the error
quantity: this.state.quantity,
price: this.state.price
};
ProductService.addProduct(data)
.then(response => {
this.setState({
id: response.data.id,
name: response.data.name,
quantity: response.data.quantity,
price: response.data.price
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
newProduct() {
this.setState({
name: "",
quantity: null,
price: null
});
}
render() {
return (
<div id="parent">
<form>
<p>Enter name:</p>
<input
type="text"
id="name"
required
value={this.state.name}
onChange={this.onChangeName}
name="name"
/>
</form>
<form>
<p>Enter price:</p>
<input
type="text"
id="price"
required
value={this.state.price}
onChange={this.onChangePrice}
name="price"
/>
</form>
<form>
<p>Enter quantity:</p>
<input
type="text"
id="quantity"
required
value={this.state.quantity}
onChange={this.onChangeQuantity}
name="quantity"
/>
</form>
<button onClick={this.saveProduct}>Enter in database</button>
</div>
);
}
}
export default Input;
When i change to for example:
saveProduct() {
var data = {
name: "test",
quantity: 123,
price: 456
};
Everything works. Ive tried diffrent things, but to me the error dont quite make sense. Anyone see where my mistake(s) are? Thank you in advance.
The problem is regarding binding this. As the ReactJS docs state:
You have to be careful about the meaning of this in JSX callbacks. In
JavaScript, class methods are not bound by default. If you forget to
bind this.handleClick and pass it to onClick, this will be undefined
when the function is actually called.
In the constructor just add following line and your code will work:
constructor(props) {
...
// This binding is necessary to make `this` work in the callback
this.saveProduct = this.saveProduct.bind(this);
}
Do this for all event handlers. Also read the doc: https://reactjs.org/docs/handling-events.html
The properties of 'this' are not always the same.
instead of using this.property. Make a global object (_this) for example and save your data in it, this way you can have your variables and properties in all functions.
for example:
import React, { Component } from 'react';
import ProductService from './ProductService';
class Input extends Component {
let _this = {};
constructor(props) {
super(props);
_this.onChangeName = _this.onChangeName.bind(_this);
_this.onChangeQuantity = _this.onChangeQuantity.bind(_this);
_this.onChangePrice = _this.onChangePrice.bind(_this);
_this.state = {
id: -1,
name: null,
quantity: null,
price: null
};
}
onChangeName(e) {
_this.setState({
name: e.target.value
});
}
onChangeQuantity(e) {
_this.setState({
quantity: e.target.value
});
}
onChangePrice(e) {
_this.setState({
price: e.target.value
});
}
saveProduct() {
var data = {
name: _this.state.name, //this is the line witch get the error
quantity: _this.state.quantity,
price: _this.state.price
};
ProductService.addProduct(data)
.then(response => {
_this.setState({
id: response.data.id,
name: response.data.name,
quantity: response.data.quantity,
price: response.data.price
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
newProduct() {
_this.setState({
name: "",
quantity: null,
price: null
});
}
render() {
return (
<div id="parent">
<form>
<p>Enter name:</p>
<input
type="text"
id="name"
required
value={_this.state.name}
onChange={_this.onChangeName}
name="name"
/>
</form>
<form>
<p>Enter price:</p>
<input
type="text"
id="price"
required
value={_this.state.price}
onChange={_this.onChangePrice}
name="price"
/>
</form>
<form>
<p>Enter quantity:</p>
<input
type="text"
id="quantity"
required
value={_this.state.quantity}
onChange={_this.onChangeQuantity}
name="quantity"
/>
</form>
<button onClick={_this.saveProduct}>Enter in database</button>
</div>
);
}
}
export default Input;
You forgot to put a bind for saveProduct().
Checkout my minimal working sample: https://codesandbox.io/s/laughing-wright-jnptu?file=/src/App.js
I agree with #tretechs arrow function don't have their own 'this' so they use this context which is the closest non-arrow parent function.
That is why we need to bind this and pass it on to the function. It is good practice to bind callback functions in the contructor function
constructor(props) {
super(props);
this.onChangeName = this.onChangeName.bind(this);
this.onChangeQuantity = this.onChangeQuantity.bind(this);
this.onChangePrice = this.onChangePrice.bind(this);
// similarly bind saveProduct
this.saveProduct = this.saveProduct.bind(this); }
I have a search and select filters on my page. The issue that I am having is that I can't seem to make the search work with multiple json values.
Example value is { "id": "1", "role": "teacher", "subject": "mathematics", "name": "Jonathan Kovinski" } and I want to be able to use key and values.
I've tried using some other question about combining json key and value into a single array and passing it to the search filter but it didn't work.
text = data.filter(info => {
return Object.keys(info).map(function(key) {
var singleOne = JSON.stringify(info[key]);
console.log(info, "This is the json one")
}).toLowerCase().match(searchString);
});
Here is a link to a JS Fiddle that I've created with all of my code.
I am trying to set my search bar to use all keys and values for searching and sorting data.
i would suggest you put the filtered data in a seperate key in the state in case you want to revert to the original result,
use the Obeject.values instead of Object.keys and filter the data in the handleChange function,
here's a working code :
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
data: [],
searchString: "",
filtered: []
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.fetchData();
}
handleChange(e) {
var value = e.target.value;
this.setState({
searchString: value,
filtered: this.state.data.filter(e =>
Object.values(e)
.join(" ")
.toLowerCase()
.match(value)
)
});
}
fetchData() {
fetch("https://api.myjson.com/bins/lo3ls")
.then(response => response.json())
.then(json => {
this.setState({
isLoaded: true,
data: json,
filtered: json
});
})
.catch(error => console.log("parsing failed", error));
}
render() {
var { isLoaded, data } = this.state;
const searchString = this.state.searchString.trim().toLowerCase();
let text = this.state.data;
console.log(text);
if (searchString.length > 0) {
text = text.filter(info => {
return info.role.toLowerCase().match(searchString);
});
}
return (
<div>
<input
type="text"
id="searchbar"
value={this.state.searchString}
onChange={this.handleChange}
placeholder="Search"
name="device"
/>
<select className="category-select" name="categories" onChange={this.handleChange}>
{data.map(info => (
<option value={info.role}>{info.role}</option>
))}
</select>
{/* map through the filtered ones*/}
{this.state.filtered.map(info => (
<div className="display">
<span className="role">Role: {info.role}</span>
<span> Name: {info.name}</span>
<span>, Subject: {info.subject}</span>
</div>
))}
</div>
);
}
}
ReactDOM.render(<Hello name="World" />, document.getElementById("container"));
Actually, I read all of your code in Fiddle, But I proffer Fuse to you. Use it inside your code in componentDidMount and implement your search. it is very easy and handy.
const options = {
shouldSort: true,
threshold: 0.6,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
"title",
"author.firstName"
]
};
const fuse = new Fuse(list, options); // "list" is the item array
const result = fuse.search(""); // put your string inside double quotation
The result is your answer.
What I have here is a function where I call the codigo, and the nombre, in the DB
table registrations. What I want to achieve is that the digital code that is like an autocomplete to fill in the name when you select the code.
enter image description here
class Matriculas extends Component {
state = {
status: "initial",
data: []
}
componentDidMount = () => {
this. getInfo()
}
getInfo= async () => {
try {
const response = await getAll('matriculas')
console.log(response.data)
this.setState({
status: "done",
data: response.data
});
} catch (error) {
this.setState({
status: "error"
});
}
};
render() {
const data = [...this.state.data];
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Input
width='150px'
type="text"
placeholder="Nombre completo"
value={data.nombre} />
</ColumnContainer>
</RowContainer>
</Container>
)
}
};
export default Matriculas;
What you most likely want to use is react-select
You can pass options to the select (which would be your names) and it will return values that match whatever you type in the search bar.
import Select from 'react-select'
const options = [
{ value: 'mike', label: 'Mike' },
{ value: 'john', label: 'John' },
{ value: 'vanessa', label: 'Vanessa' }
]
const MyComponent = () => (
<Select options={options} />
)
So you can take that example, and the examples in the link, and put it in your code:
import Select from 'react-select'
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Select
value={this.state.nameValue}
onChange={event => {this.setState({nameValue: e.value})}
options={options} />
</ColumnContainer>
</RowContainer>
</Container>
When using onChage, it returns an event, which has the value of the selected name. You can use that to set the state's nameValue, and then use that name value in the rest of your component as well
Once you get this up and running, it also worth looking at the async select, which allows you to give an async function that returns values (your getInfo function, for example)
-- edit --
If you want to define the onChange event elsewhere, it would look like this:
handleChange = event => {
// event.value will be the value of the select
this.setState({optionSelected: event.value});
}
and then in your onChange, tell it that is the function you want but do not invoke it (don't write it with parentheses):
<Select
value={this.state.optionSelected}
onChange={this.handleChange}
options={options} />
The code below is only working when I remove the componentWillMount that uses localStorage. With usage localStorage it gives a mistake
this.state.interests.map is not a function
I tried to move usage of localStorage out of component but it won't help. I suppose that using local storage somehow changes this.state.interests that they stop being an array.
let interests = ["Музыка", "Компьютеры", "Радио"]
let ListOfInterest = React.createClass({
getInitialState: function() {
return {value: '', interests: interests};
},
componentWillMount() {
let local = localStorage.getItem('interests')
if (local) {
this.setState({interests: local});
} else {
localStorage.setItem('interests', this.state.interests)}
},
deleteInterest(key) {
delete interests[key]
this.setState(this.state) // without this line the page will not re-render
},
addInterest() {
interests.unshift(this.state.value)
this.setState({value: ''})
},
handleChange(event) {
this.setState({value: event.target.value})
},
render() {
return <div className="interests">
<b>Интересы</b>
<br/>
{this.state.interests.map((int, index) => {
return <button onClick={() => {
this.deleteInterest(index)
}} key={index} className="btn-interest">{int}</button>
})}
<input type='text' placeholder="Add" value={this.state.value} onChange={(e) => this.handleChange(e)}/>
<button onClick={() => {
this.addInterest()
}} className="add">Add interest</button>
</div>
}
})
<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>
You have several issues in your example
in localStorage.setItem second argument have to be a String, you can not store Array(when you do it, in storage will be string separated by coma because called method toString - [1, 2, 3].toString() ), you need stringify array before set to Storage
keyValue A DOMString containing the value you want to give the
key you are creating/updating.
localStorage.setItem(
'interests', JSON.stringify(this.state.interests)
)
and parse when get value
let local = JSON.parse(localStorage.getItem('interests'));
this.setState(this.state) this is not good way to update state, you need update state like so
deleteInterest(key) {
this.setState({
interests: this.state.interests.filter((el, i) => i !== key)
})
},
addInterest() {
this.setState({
value: '',
interests: this.state.interests.concat(this.state.value)
});
},
Example