import React from 'react';
import './MenuCard.css';
class MenuCard extends React.Component {
constructor(props) {
super(props);
this.state = {
showButton: false,
hideButton: true,
aValue: 1,
breads: [],
category: [],
ids: 0
};
this.onShowButton = this.onShowButton.bind(this);
}
onShowButton = (id) => {
this.setState({
showButton: !this.state.showButton,
hideButton: !this.state.hideButton
}));
}
onValueIncrease = () => {
this.setState({aValue: this.state.aValue + 1});
}
onValueDecrease = () => {
this.setState({aValue: this.state.aValue - 1});
}
render() {
return (
<div>
{this.state.category.map(types => {
return (<div>
<div className="menu-head">{types}</div>
< div className="container-menu">
{this.state.breads.map((d, id)=> {
if (d.category === types) {
return (
<div className="content">
<div className="items"> {d.item_name}</div>
<div className="prices"> {d.price} Rs.</div>
<button id ={id} onClick={() => this.onShowButton(d.id)}
hidden={this.state.showButton}
className="add-menu-btn"> add
</button>
<span key={d.id} hidden={this.state.hideButton}>
<button id={d.id} className="grp-btn-minus"
onClick={this.state.aValue <= 1 ? () => this.onShowButton(d.id) : () => this.onValueDecrease(d.id)}>-
</button>
<input className="grp-btn-text" type="text"
value={this.state.aValue} readOnly/>
<button id={d.id} className="grp-btn-plus"
onClick={() => this.onValueIncrease(d.id)}>+
</button>
</span>
</div>
)
}
})}
</div>
</div>)
})}
</div>
)
}
There are multiple buttons according to items 1.
And here the problem when I click on single button all get updated I need only a single button to click with a single update 2
You need to keep the values in an array in the state, i.e:
values: [
{ id: 1, value: 20},
{ id: 2, value: 1}
]
If you then need to set the state, could look like this:
const values = Object.assign({}, this.state.values, { [id]: value })
this.setState({ values })
To get the value from state:
const value = this.state.values[id]
Related
I am trying to reset Core UI ReactJS CMultiSelect component with Reset button. I have used setState method to reset the value. As soon as I click on the reset button, the state value changes, but immediately onChange method of CMultiSelect is called and the existing value is retained.
Below is the code snippet I'm trying.
import React from 'react'
import { CRow, CMultiSelect, CFormInput, CButton } from '#coreui/react-pro'
class TestForm extends React.Component<{}, { textVal: string; dropdownVal: string[] }> {
constructor(props: any) {
super(props)
this.state = { textVal: '123', dropdownVal: [] }
}
setTextVal(newVal: string) {
this.setState({ textVal: newVal })
}
setTest(newVal: string[]) {
this.setState({ dropdownVal: newVal })
}
render() {
return (
<div className="row m-5">
<div className="col-sm-6">
<CFormInput
type="text"
value={this.state.textVal}
onChange={(evt) => {
this.setTextVal(evt.target.value)
}}
></CFormInput>
</div>
<div className="col-sm-6">
<CMultiSelect
multiple={false}
options={[
{
value: '1',
text: '1',
selected: this.state.dropdownVal.indexOf('1') >= 0,
},
{
value: '2',
text: '2',
selected: this.state.dropdownVal.indexOf('2') >= 0,
},
{
value: '3',
text: '3',
selected: this.state.dropdownVal.indexOf('3') >= 0,
},
]}
onChange={(val) => {
console.log('on change called', val)
this.setTest(
val.map((x) => {
return x.value.toString()
}),
)
}}
></CMultiSelect>
</div>
<div className="col-sm-6">
<CButton
className="mt-3"
type="reset"
value="Reset"
onClick={() => {
this.setTest([])
this.setTextVal('')
}}
>
Reset
</CButton>
</div>
</div>
)
}
}
export default TestForm
When I hit the reset button, value of the text field resets, but not the multi-select dropdown.
try calling the reset method on the component instance. You can do this by saving a reference to the CMultiSelect instance in your component's state, and then calling the reset method on that instance in your reset handler.
class MyComponent extends React.Component {
state = {
select: null,
};
handleReset = () => {
this.state.select.reset();
}
render() {
return (
<div>
<CMultiSelect
ref={(select) => this.setState({ select })}
onChange={this.handleChange}
/>
<button onClick={this.handleReset}>Reset</button>
</div>
);
}
}
I would like to get the values of some input on change when they are inside a map loop function. This is my component:
import React, { Component } from "react";
class ArrayComponent extends Component {
constructor(props) {
super(props);
this.state = { objects: [] };
}
handleChange(index, id, e) {
// what to put here ?
// want to have a state like :
// [{index: e.target.value, key: id}, {index: e.target.value, key: id}, ...]
}
render() {
return (
<Form onSubmit={this.onSubmit}>
{items.map((item, index) => (
<div>
{item.name}
<input
key={index}
value={this.state.objects[index]}
onChange={this.handleChange.bind(this, index, item.id)}
/>
</div>
))}
<Button>
Update
</Button>
</Form>
);
}
}
export default ArrayComponent;
I want to have a state like :
[{index: e.target.value, id: id}, {index: e.target.value, id: id}, ...] It means that If they are four inputs I want to have a state like above for four inputs.
Are you looking for something like this:
constructor(props) {
super(props);
this.state = { objects: {} };
}
handleChange(event, index, id) {
this.setState((state) => {
const newObject = {...state.objects};
newObject[`${index}`] = {value: event.target.value, key: id}
return {objects: newObject }
});
}
render() {
return (
<Form onSubmit={this.onSubmit}>
{items.map((item, index) => (
<div>
{item.name}
<input
key={item.id}
value={this.state.objects[`${index}`]?.value || ''}
onChange={(event) => this.handleChange(event, index, item.id)}
/>
</div>
))}
<Button>
Update
</Button>
</Form>
);
}
}
You should avoid setting map index values as component keys. So I removed index and just used the item id as the key prop.
Edit - Removed index
You can remove index all together:
handleChange(event, id) {
this.setState((state) => {
const newObject = {...state.objects};
newObject[`${id}`] = {value: event.target.value, key: id}
return {objects: newObject }
});
}
//........
value={this.state.objects[`${item.id}`]?.value || ''}
onChange={(event) => this.handleChange(event, item.id)}
iam new to react i tried to fix it by using bind method on my other projects. then i heard that binding is not required while using arrow function. so i now trying to use arrow function but getting this error all the time
import React, { Component } from "react";
class Counter extends Component {
render() {
return (
<div>
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button onClick={()=>this.props.handleIncrement(this.props.counter)}
className="btn btn-secondary btn-sm m-2 p-3">Increment</button>
<button onClick={()=>this.props.onDelete(this.props.id)} className="btn btn-danger btm-sm">Delete</button>
</div>
);
}
formatCount(){
const { value } = this.props.counter;
return value === 0 ? "Zero" : value;
}
getBadgeClasses(){
let classes="badge p-3 badge-";
classes+= this.props.counter.value===0 ? "warning":"primary";
return classes
}
}
export default Counter;
i have imported counter into this counters.jsx.
import React, { Component } from 'react';
import Counter from "./counter"
class Counters extends Component {
state = {
counters :[
{id:1 , value:2},
{id:2 , value:0},
{id:3 , value:4},
{id:4 , value:0},
{id:5 , value:5}
]
}
handleIncrement=counter=>{
console.log(counter);
}
handleReset=()=>{
}
handleDelete=counterId=>{
const counters=this.state.counters.filter(c=>c.id!==counterId);
this.setState({counters});
}
render() {
return (
<div>
{this.state.counters.map(counter =>
<Counter key={counter.id} onDelete={this.handleDelete}
onIncrement={this.handleIncrement} value={counter.value} id={counter.id} />
)}
</div>
);
}
}
export default Counters;
while compiling iam getting the error TypeError: Cannot read property 'value' of undefined
. i used arrow function instead of binding the function enter image description here
You're sending the prop as value but in the child component you're accessing it as counter.value.
this.props.counter.value should be this.props.value. Or else send the counter to the child.
Issues
counter.value is passed to value prop of Counter, but accessed as this.props.counter.value.
this.handleIncrement is passed to onIncrement prop, but accessed as this.props.handleIncrement.
this.props.handleIncrement passes this.props.counter as argument, but both are undefined.
Solutions
Access correctly this.props.value.
formatCount() {
const { value } = this.props; // <-- destructure correctly
return value === 0 ? "Zero" : value;
}
getBadgeClasses() {
let classes = "badge p-3 badge-";
classes += this.props.value === 0 ? "warning" : "primary"; // <-- access correctly
return classes;
}
I suggest converting your handlers in Counters to curried functions so you don't need to pass the id explicitly. I also suggest using functional state updates so counts are correctly updated from the previous state.
handleIncrement = (id) => () => {
this.setState((prevState) => ({
counters: prevState.counters.map((counter) =>
counter.id === id
? {
...counter,
value: counter.value + 1
}
: counter
)
}));
};
handleDelete = (id) => () => {
this.setState((prevState) => ({
counters: prevState.counters.filter((counter) => counter.id !== id)
}));
};
Attach the modified handlers and access accordingly. Here we will pass the id value to the curried handler.
Counters
<div>
{this.state.counters.map((counter) => (
<Counter
key={counter.id}
onDelete={this.handleDelete(counter.id)} // <-- pass id
onIncrement={this.handleIncrement(counter.id)} // <-- pass id
value={counter.value}
/>
))}
</div>
Counter
<div>
...
<button
onClick={this.props.onIncrement} // <-- attach handler callback
className="btn btn-secondary btn-sm m-2 p-3"
>
Increment
</button>
<button
onClick={this.props.onDelete} // <-- attach handler callback
className="btn btn-danger btm-sm"
>
Delete
</button>
</div>
Full Code
class Counter extends Component {
render() {
const { onDelete, onIncrement, value } = this.props;
return (
<div>
<span className={this.getBadgeClasses()}>{value}</span>
<button
onClick={onIncrement}
className="btn btn-secondary btn-sm m-2 p-3"
>
Increment
</button>
<button onClick={onDelete} className="btn btn-danger btm-sm">
Delete
</button>
</div>
);
}
getBadgeClasses() {
const { value } = this.props;
let classes = "badge p-3 badge-";
classes += value ? "primary" : "warning";
return classes;
}
}
class Counters extends Component {
state = {
counters: [
{ id: 1, value: 2 },
{ id: 2, value: 0 },
{ id: 3, value: 4 },
{ id: 4, value: 0 },
{ id: 5, value: 5 }
]
};
handleIncrement = (id) => () => {
this.setState((prevState) => ({
counters: prevState.counters.map((counter) =>
counter.id === id
? {
...counter,
value: counter.value + 1
}
: counter
)
}));
};
handleReset = () => {};
handleDelete = (id) => () => {
this.setState((prevState) => ({
counters: prevState.counters.filter((counter) => counter.id !== id)
}));
};
render() {
const { counters } = this.state;
return (
<div>
{counters.map(({ id, value }) => (
<Counter
key={id}
onDelete={this.handleDelete(id)}
onIncrement={this.handleIncrement(id)}
value={value || "Zero"}
/>
))}
</div>
);
}
}
I have five Users in the array.
The code below displays each users info from the arrays when pop up button is clicked and everything works fine.
Now I have created a form to update each user's age based on their respective person Id on form submission via call to nodejs
backend. Am actually getting the result from nodejs backend..
Here is my issue.
Each time I entered age in the input and click on submission button Eg. for user 1. Instead of the age result to
appear near that very user 's name in the space provided in the button, it will appears on the body of the page as can be seen from
screenshots provided.
If call it as props For instance {this.props.messages.personAge}
as per below
<button
onClick={() => this.open(this.props.data.id, this.props.data.name)}
>
(Age should Appear Here-- ({this.props.messages.personAge})--)
{this.props.data.name}
</button>
It shows error
TypeError: Cannot read property 'personAge' of undefined
at User.render
Here is how am getting the response from nodejs server
componentDidMount(){
this.socket = io('http://localhost:8080');
this.socket.on('response message', function(data){
addAge(data);
});
const addAge = data => {
console.log(data);
//this.setState({messages: [...this.state.messages, data]});
this.setState({messages: [data]});
};
}
below is how am displaying the age result for each unique user
{this.state.messages.map((message, i) => {
//if (message.personId == this.props.data.id) {
//if (message.personId == person.id) {
if (message.personId) {
return (
<div key={i}>
<div>
({message.personAge}--years)
</div>
</div>
)
}
})}
</ul>
Here is the Entire Code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import { Link } from 'react-router-dom';
import axios from 'axios';
import io from "socket.io-client";
class User extends React.Component {
open = () => this.props.open(this.props.data.id, this.props.data.name);
render() {
return (
<React.Fragment>
<div key={this.props.data.id}>
<button
onClick={() => this.open(this.props.data.id, this.props.data.name)}
>
(Age should Appear Here-- ({this.props.messages})--)
{this.props.data.name}
</button>
</div>
</React.Fragment>
);
}
}
class OpenedUser extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: false,
personId: '',
personAge: '',
};
}
componentDidMount(){
this.socket = io('http://localhost:8080');
var userId= this.props.data.id;
}
sendPost = (personId,personAge) => {
alert(personId);
alert(personAge);
this.socket.emit('messageUpdate', {
personId: personId,
personAge: personAge,
});
this.setState({personId: ''});
this.setState({personAge: ''});
}
toggleHidden = () =>
this.setState(prevState => ({ hidden: !prevState.hidden }));
close = () => this.props.close(this.props.data.id);
render() {
return (
<div key={this.props.data.id} style={{ display: "inline-block" }}>
<div className="wrap_head">
<button onClick={this.close}>close</button>
<div>user {this.props.data.id}</div>
<div>name {this.props.data.name}</div>
{this.state.hidden ? null : (
<div className="wrap">
<div className="wrap_body">Update Age Info</div>
<div> </div>
<div>
<label></label>
<input type="text" placeholder="personAge" value={this.state.personAge} onChange={ev => this.setState({personAge: ev.target.value})}/>
<br/>
<span onClick={ () => this.sendPost(this.props.data.id, this.state.personAge)} className="btn btn-primary">Update Age</span>
</div>
</div>
)}
</div>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
showingAlert_UserTyping: false,
shown: true,
activeIds: [],
messages: [],
data: [
{ id: 1, name: "user 1" },
{ id: 2, name: "user 2" },
{ id: 3, name: "user 3" },
{ id: 4, name: "user 4" },
{ id: 5, name: "user 5" }
]
};
}
componentDidMount(){
this.socket = io('http://localhost:8080');
this.socket.on('response message', function(data){
addAge(data);
console.log(' am add message' +data);
});
const addAge = data => {
console.log(data);
//this.setState({messages: [...this.state.messages, data]});
this.setState({messages: [data]});
};
} // close component didmount
toggle() {
this.setState({
shown: !this.state.shown
});
}
open = (id,name) => {
this.setState(prevState => ({
activeIds: prevState.activeIds.find(user => user === id)
? prevState.activeIds
: [...prevState.activeIds, id]
}));
};
close = id => {
this.setState(prevState => ({
activeIds: prevState.activeIds.filter(user => user !== id)
}));
};
renderUser = id => {
const user = this.state.data.find(user => user.id === id);
if (!user) {
return null;
}
return (
<OpenedUser messages={this.state.messages}
key={user.id}
data={user}
close={this.close}
/>
);
};
renderActiveUser = () => {
return (
<div style={{ position: "fixed", bottom: 0, right: 0 }}>
{this.state.activeIds.map(id => this.renderUser(id))}
</div>
);
};
render() {
return (
<div>
<ul>
{this.state.messages.map((message, i) => {
//if (message.personId == this.props.data.id) {
//if (message.personId == person.id) {
if (message.personId) {
return (
<div key={i}>
<div>
({message.personAge}--years)
</div>
</div>
)
}
})}
</ul>
{this.state.data.map(person => {
return (
<User key={person.id} data={person} open={this.open} />
);
})}
{this.state.activeIds.length !== 0 && this.renderActiveUser()}
</div>
);
}
}
Here is how I solved the issue:
I created a const resultdata and using map() and Filter() function.
Here is how I initialized the the variable resultdata and then pass it within state.data.map() method
const resultdata = this.state.messages.filter(res => res.personId == person.id).map(res => res.personAge));
I have a text box and on typing a name i get a list of options via an api call. I then populate a list and on click of a list item i am trying to fill the text in the input box. Firstly when i add a value prop to the input element i am unable to to type anything in the text box. Also on clicking the list item the value of the text doesnt update. Can someone tell me what im doing wrong here
class AutoCompleteSearch extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: []
}
}
autoSearchInputChange(e) {
let searchValue = e.target.value;
if (!searchValue.trim()) {
this.setState({ value : '', suggestions: [] })
return ;
}
if (searchValue.length >= 3) {
setTimeout(() => {
searchDoctorByName(searchValue).then((response) => {
this.setState({ value : searchValue, suggestions: response.data })
})}, 1000);
}
}
selectItemFromList(doctorObject) {
this.setState({
value: doctorObject.name ,
suggestions: [doctorObject]
});
console.log(this.state);
}
render() {
let renderItems = () => {
let listItems = this.state.suggestions.map((suggestion, index) => {
let doctorObject = suggestion;
return (
<li onClick={() => this.selectItemFromList(doctorObject)} key={index}>
{doctorObject.name}
</li>
);
});
return (
<div>
<ul className="doctor-list">
{listItems}
</ul>
</div>
);
}
return (
<div className="form-group">
<label className="control-label form-label col-md-4" htmlFor="auto-complete-search">Doctor Name</label>
<div className="col-md-20">
<input className="custom-input"type="text" ref="test" id="auto-complete-search" required
placeholder="Enter Doctor name"
onChange={(e) => this.autoSearchInputChange(e)}
/>
{this.state.suggestions.length > 0 ? renderItems() : null}
</div>
</div>
);
}
}
export default AutoCompleteSearch;