How to display selected Listbox value in ReactJS from state - javascript

What I want to do is select an item from the ListBox and display it in the same WebPage. The ListBox renders correctly, but when I try to Output the selected value, it shows target undefined. Refer comments in code.
I tried using event.target.value to retrieve the value selected which has always worked for me so far, but it says event.target is undefined and the value is inaccessible.
Note : I tried using Grommet for this app for styling, "List", "Box", "ListItem" are all grommet components.
Any help will be highly appreciated.
import React, {Component} from "react";
import Box from "grommet/components/Box";
import List from "grommet/components/List";
import ListItem from "grommet/components/ListItem";
const lb = [];
class ListBox extends Component{
state = {
products: [ //Array of Products
"Product1",
"Product2",
"Product3"
],
selected: null //State Variable where selected item will be stored
};
contents () {
for (let i = 0; i < this.state.products.length; ++i){
lb[i] =
<ListItem justify = "between" key = {i}>
<span>
{this.state.products[i]}
</span>
</ListItem>
}
}
itemSelected (event) {
console.log(event.target); //SHOWS TARGET UNDEFINED in Console Window
let temp = {
selected: event.target
}
this.setState(temp);
}
render () {
this.contents();
return (
<Box>
<List
selectable={true}
onSelect = { //The function call on selecting an item
(event) => {
this.itemSelected(event);
}
} >
{lb}
</List>
<p>Selected Product : {this.state.selected}</p>
</Box>
);
}
}
export default ListBox;

grommet give you selected item index not event.
please check official document.
http://grommet.io/docs/list
itemSelected (index) {
console.log(index);
let temp = {
selected: this.state.products[index]
}
this.setState(temp);
}

Related

how to add dinamically components with data, on objects list with react js

I have a component list, in this case is a MUI chips, which has some props (label, callback) and I need to add them to my list when onClick event is triggered.
The chip is going to have a label, which is a name selected from a dropdown menu.
I found quite difficult to have a unique chip with the name selected.
//my components list
const [chipList, setChip] = useState([{ chip: "" }]);
const addNewCategory = () => {
if (chipList.length < 5) {
setChip([...chipList, { chip: "" }]);
}
};
//my map to render the component
{chipList.map((widget, index) => (
<CategoryChip key={index} label={subCategory} onDelete={() => handleDelete(index)} />
))}
I am quite sure I have to pass the label inside my useState([{ chip: "" }]) and yes I know, for the moment my chips has all same name because of the label attribute
You don't need to map() your chipList if your intent is to only show one. The one that is selected.
I'm assuming your subCategory state or prop is the chip info that you chose from the dropdown.
You can use findIndex() to show CategoryChip related with that choice.
export default YourComponent = () => {
const [chipList, setChip] = useState([{ chip: "" }]);
const addNewCategory = () => {
if (chipList.length < 5) {
setChip([...chipList, { chip: "" }]);
}
};
...
const renderSelectedChip = () => {
const foundChipIndex = chipList.findIndex(el => el.chip === subCategory);
// I checked for subCategory just to make sure that empty string wasn't a selectable option, but you can remove it if you want
if (!subCategory || foundChipIndex === -1) {
return <></>;
}
return <CategoryChip label={subCategory} onDelete={() => handleDelete(foundChipIndex)} />
))} />
}
return (
<>
...
{renderSelectedChip()}
</>
)
}

React - How to use a key to setState on a specific object in an array when modifying a property?

I have created a menu card that will show a card for each object in an array. The object holds details about that menu item. I am looking to affect one property of that menu item but I am unsure how to potentially use the key or an id to affect only that item. How can I dynamically select an object from an array based on its key or some other unique identifier?
Right now as a stand-in I use handleCategory to affect the first object in the array. I am looking instead to affect the object based on which Item card I am using.
Below is what my code looks like (basic example):
Home.js
import React, { Component } from 'react';
import { Form, Button } from 'react-bootstrap';
import Items from './items'
import Navbar from './navbar'
class Home extends Component {
constructor() {
super();
this.state = {
value: 'enter',
condimentCount: 5,
menuItems: [
{
"id": 1234,
"item_name": 'chow mein',
"category_id": 'meal'
},
{
"id": 1235,
"item_name": '',
"category_id": 'meal'
}
]
};
this.handleCategory = this.handleCategory.bind(this);
}
handleCategory = (event, id) => {
console.log('changing meal type', event.target.value)
//this.setState({value: event.target.value});
// 1. Make a shallow copy of the items
let items = [...this.state.menuItems];
// 2. Make a shallow copy of the item you want to mutate
let item = {...items[0]};
// 3. Replace the property you're intested in
item.category_id = event.target.value;
// 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
items[0] = item;
// 5. Set the state to our new copy
this.setState({menuItems: items});
}
render() {
let menuItems = null;
if (this.state.menuItems) {
menuItems = (
<div>
{
this.state.menuItems.map((item, i) => {
return <div key={i}>
<MenuItem
key={item.id} //IS THIS CORRECT?
item_name={item.item_name}
category_id={item.category_id}
handleCategory={this.handleCategory}
/>
</div>
})
}
</div>
)
}
return (
<div>
<Navbar />
<div>
{Items}
</div>
</div>
);
}
}
export default Home;
Items.js
import React, { Component } from 'react';
import classes from './items.module.css'
const Items = (props) => {
return (
<div className={classes.cards}>
<form>
<label>
What kind of meal item is this (e.g. meal, drink, side):
<select value={props.category_id} onChange={props.handleCategory}>
<option value="meal">meal</option>
<option value="drink">drink</option>
<option value="side">side</option>
</select>
</label>
</form
</div>
)
}
export default Items;
I'm not really sure what you're trying to do in handleCategory, but I understand your question I believe.
You can use the index from map to access the menu items in handleCategory.
this.state.menuItems.map((item, i) => {
return <div key={i}>
<MenuItem
item_name={item.item_name}
category_id={item.category_id}
handleCategory={e => this.handleCategory(e, i)}
/>
</div>
})

How to handle checked in input type='checkbox' in React?

I have a group of checkboxes, some of them are pre-checked and some will be updated from the user. problem is the checkboxes render fine in initial render but they don't change on click. The value of 'checked' gets updated on change (onChange)
<input
type = 'checkbox'
style = {{margin : '0'}}
checked = {this.state[elem]}
value = {elem}
onChange = {this.checked}
/> {elem}
and in the checked method
checked = e => {
this.setState({
[e.target.value] : !this.state[e.target.value]
})
}
You haven't shared with us how you are generating these inputs, but the primary issue has to do with using a single source of truth for all your inputs. You want to give each input, particularly the object that corresponds to that input, their own checked-state.
Considering the following example with working codesandbox: https://codesandbox.io/s/relaxed-feynman-1thb8
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const arr = [
{ val: "Cat", checked: false },
{ val: "Dog", checked: true },
{ val: "Turtle", checked: false }
];
class App extends React.Component {
state = {
arr: arr
};
handleCheck = e => {
const arrCopy = [...this.state.arr];
const itemToUpdate = arrCopy.find(item => item.val === e.target.value);
itemToUpdate.checked = !itemToUpdate.checked;
this.setState({
arr: arrCopy
});
};
createInputs = () => {
const { arr } = this.state;
return arr.map(elem => {
return (
<div>
<input
type="checkbox"
style={{ margin: "0" }}
checked={elem.checked}
value={elem.val}
onChange={this.handleCheck}
/>
{elem.val}
</div>
);
});
};
render() {
return <div>{this.createInputs()}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
In the above code, we organize your state-data as in array of objects, making it easier for you to render the mark-up AND keep track of the checked state of each input. Then in the handleCheck function, we identify the checked item, find its corresponding object in the array and toggle the checked state of that item.
Set input's name property to property equivalent to that of state's key. Also your way of accessing object's key as this.state[elem] is inappropriate as elem is not a variable(that should contain string as state's key).
I have prepared codesandbox for use case. Here is codesandbox link for the example : https://codesandbox.io/embed/clever-colden-2ydb6
Updated code:
checked = e => {
const checked = e.target.checked;
const name = e.target.name;
this.setState({
[name]: checked
});
};
render() {
return (
<div>
<input
type="checkbox"
style={{ margin: "0" }}
// set name prop to equivalent to state's property
name="elem"
// either use - this.state['elem'] or this.state.elem
checked={this.state.elem}
// value = {elem} no need of this
onChange={this.checked}
/>
Is checked : {this.state.elem ? "yes" : "no"}
</div>
);
}

How can I stop this wierd output of my lists in react

I want to make each element in a list clickable separately. I have an array of divs which I will loop into an array soon but for simplicity, I just hardcoded them into it(I am going to add more elements once I figure this out). When I click on the list item div, I want it to turn that Item into the text: "clicked".
I want to keep the files separate because this app will get big and I'm planning to add much more.
App.js
import React, { Component } from 'react';
import './App.css';
import Comp from './Comp';
class App extends Component {
state = {
list: [
"gameplay",
"visuals"
]
}
changetext = event =>{
this.setState({list: event.target.textContent = "clicked"});
}
render() {
return (
<div>
<Comp list = {this.state.list}
changetext = {this.changetext}/>
</div>
);
}
}
export default App;
Comp.js
The problem here is that when I click on a list item, The event.target.textContent is inputting {props.list[0]} and {props.list[1]} into the event object and turn both elements into c and l respectively.. both are the first and second elements in the string array "clicked".
The strange thing is, when I click the c or the l the second time, they act as I wanted them to and separately turn into clicking. So the question is, How can I achieve this without the initial hiccup? Let me know if you need set up information.
import React from 'react';
const Comp = props => {
let listarr = [];
listarr[0] = <div key = {0} onClick = {props.changetext}{props.list[0]}
listarr[1] = <div key = {1} onClick = {props.changetext}>{props.list[1]}
</div>
return(
<div>{listarr}</div>
);
}
export default Comp;
You have a couple of syntax errors. If you want to change the text to "clicked" you can do it like this:
const Comp = props => {
let listarr = [];
listarr.push(<div key={0} onClick={props.changetext}>{props.list[0]}</div>);
listarr.push(<div key={1} onClick={props.changetext}>{props.list[1]}</div>);
return (
<div>{listarr}</div>
);
}
class App extends Component {
state = {
list: [
"gameplay",
"visuals"
]
}
changetext = event => {
const { textContent } = event.target;
// Always use the callback syntax for setState when you need to refer to the previous state
this.setState(prevState => ({
list: prevState.list.map(el => textContent === el ? "clicked" : el)
}));
}
render() {
return (
<div>
<Comp list={this.state.list}
changetext={this.changetext} />
</div>
);
}
}
Just change the method you are passing as a property to be:
this.changetext.bind(this)
so it will look like this:
<div>
<Comp list = {this.state.list}
changetext = {this.changetext.bind(this)}/>
</div>
Or your other option could be to do this in the constructor:
constructor() {
super();
this.changetext.bind(this);
}
...
render() {
<div>
<Comp list = {this.state.list}
changetext = {this.changetext}/>
</div>
}

can't reach unordered list if it's created in the return of the render function

I need to get the same result I got, just by using react
I can't seem to find a way to reach the unorderd list if I creat it in the return of the render function, I'm new to react and just found out that getElementById just returns null in this case
(create an unordered list, Clicking on one of the items toggle the color of the item's text color)
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
var defaultColor = 'black'
var specialColor = 'red'
var itemList =[1,2,3,4,5]
var id = document.getElementById('list')
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
}
function toggleColor(ctrl)
{
if(ctrl.color == specialColor){
ctrl.color = defaultColor
}
else{
ctrl.color = specialColor;
}
}
function createLiWithColorToggle(item){
var newLi= document.createElement('li')
var newFont= document.createElement('font')
newFont.innerHTML = item
newFont.onclick = ()=>toggleColor(newFont)
newLi.appendChild(newFont)
id.appendChild(newLi)
}
itemList.forEach((item)=>{
createLiWithColorToggle(item)
})
};
render() {
return (
<div>
</div>
);
}
}
render(<App/>, document.getElementById('root'));
HTML file:
<div id="root"></div>
<ul id = "list"></ul>
When working with react you shouldn't try to create DOM elements yourself. Your components define the desired markup given the current state of your app. Rendering an unordered list could e.g. look like this:
import React, { Component } from "react";
import { render } from "react-dom";
const defaultColor = "black";
const specialColor = "red";
// your initial data for the list
const initialItems = [1, 2, 3, 4, 5];
class App extends Component {
state = {
items: initialItems,
selected: null
};
handleClick = item => this.setState({ selected: item });
render() {
const { items, selected } = this.state;
return (
<ul>
{items.map(item => (
<li
key={item}
onClick={() => this.handleClick(item)}
style={{
cursor: "pointer",
color: item === selected ? specialColor : defaultColor
}}
>
{`Item ${item}`}
</li>
))}
</ul>
);
}
}
render(<App />, document.getElementById("root"));
Working example on codesandbox
As you can see it is not necessary to create DOM elements yourself at all. React will handle all that for you. You just have to define the markup given the current state of the component.
You are not returning anythig (just an empty div) in the render method, you need to include something inside the return() block:
render() {
return (
<div>
// Return something
</div>
);
For more info, check docs

Categories

Resources