Now I do not really understand you. Sorry, I just started this whole study not so long ago. I’ll try to explain again what I can’t do.
I have an empty object and an object with data with the same structure.
data: [
{id: 1, title: "title1"},
{id: 2, title: "title1"},
{id: 3, title: "title3"},
{id: 4, title: "title4"},
{id: 5, title: "title3"}
],
item: [
{
itemId: "",
itemname: ""
}
]
And I have select and textarear. Select have data, textarear empty. Textarear displays title.
I want to press a button. Selected item from select. copied to textarear (title only), and also itemId - this selected element id: 5 and itemname - the same title: "title3" element, was recorded in item [].
https://codesandbox.io/s/priceless-hermann-g9flw
Please do check now
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
id: null,
title: "",
filmItem: "",
listFilms: [],
data: [
{ id: 1, title: "title1" },
{ id: 2, title: "title2" },
{ id: 3, title: "title3" },
{ id: 4, title: "title4" }
],
item: []
};
this.onChange = this.onChange.bind(this);
this.onChangeArea = this.onChangeArea.bind(this);
this.addFilm = this.addFilm.bind(this);
this.choice = this.choice.bind(this);
}
addFilm(film) {
const selectedData = this.state.data.find(item => item.id == film);
console.log(selectedData);
this.setState({
listFilms: [...this.state.listFilms, selectedData.title],
item: [
...this.state.item,
{ itemId: selectedData.id, itemname: selectedData.title }
]
});
}
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
onChangeArea = e => {
this.setState({ [e.target.name]: e.target.value.split("\n") });
};
choice(title) {
this.setState({ filmItem: title });
}
render() {
return (
<div className="App">
<div className="row App-main">
<div>
<select name="filmItem" size="4" onChange={e => this.onChange(e)}>
{this.state.data.map(film => (
<option key={film.title} value={film.id}>
{film.title}
</option>
))}
</select>
</div>
<div>
<button
className="editButton"
onClick={() => this.addFilm(this.state.filmItem)}
>
button
</button>
</div>
<div>
<textarea
name="films"
onChange={this.onChangeArea}
value={this.state.listFilms.map(r => r).join("\n")}
/>
</div>
<div>
<input type="text" name="text-input" onChange={this.onChange} />
</div>
</div>
<pre style={{ whiteSpace: "pre-wrap" }}>
{JSON.stringify(this.state)}
</pre>
</div>
);
}
}
export default App;
Related
so i am trying to make a filter for my products. And for that i initialised a state in a component called Product that has all the filters. And then, based on the input of the checkboxes that are in a child component that is called Category, the filter state changes.
here is my code :
Product.jsx:
import { useState } from "react";
import Filter from "./Filter.jsx";
import styles from "../styles/products.module.css";
const products = {
prod1: {
id: 1,
name: "prod1",
category: "laptops",
price: "80",
description: { brand: "lenovo", processor: "intel core i7" },
},
prod2: {
id: 2,
name: "prod2",
category: "laptops",
price: "10",
description: { brand: "lenovo", processor: "intel core i7" },
},
prod3: {
id: 3,
name: "prod3",
category: "laptops",
price: "100",
description: { brand: "msi", processor: "intel core i5" },
},
prod4: {
id: 4,
name: "prod4",
category: "laptops",
price: "200",
description: { brand: "msi", processor: "intel core i3" },
},
prod5: {
id: 5,
name: "prod5",
category: "phones",
price: "50",
description: { brand: "samsung", ram: "8gb", storage: "64gb" },
},
prod6: {
id: 6,
name: "prod6",
category: "phones",
price: "50",
description: { brand: "infinix", ram: "4gb", storage: "128gb" },
},
prod7: {
id: 7,
name: "prod7",
category: "phones",
price: "50",
description: { brand: "oppo", ram: "8gb", storage: "256gb" },
},
prod8: {
id: 8,
name: "prod8",
category: "accessories",
price: "99",
description: { type: "keyboard" },
},
prod9: {
id: 9,
name: "prod9",
category: "accessories",
price: "75",
description: { type: "mouse" },
},
};
function Products() {
const filter = {
laptops: {
brand: [],
processor: [],
},
phones: {
brand: [],
ram: [],
storage: [],
},
accessories: {
type: [],
},
};
const [filterList, setFilterList] = useState(filter);
const [category, setCategory] = useState("all");
const [range, setRange] = useState(9999);
const getFilterState = () => {
return filterList;
};
const setFilterState = (list) => {
setFilterList(list);
};
return (
<div className={styles.productsContainer}>
<Filter
range={[range, setRange]}
category={[category, setCategory]}
getfilterList={getFilterState}
setFilterState={setFilterState}
/>
<table className={styles.productsListContainer}>
<tr className={styles.tableColumn}>
<th className={styles.tableRow}>id</th>
<th className={styles.tableRow}>Name</th>
<th className={styles.tableRow}>price</th>
</tr>
{Object.keys(products).map((key) => {
let flag = true;
const product = products[key];
const categ = product.category;
if (
(categ === category || category === "all") &&
product.price <= range
) {
Object.entries(filter[categ]).forEach(([key, value]) => {
if (
!(
value.length === 0 || value.includes(product.description[key])
)
) {
flag = false;
}
});
if (flag) {
return (
<tr className={styles.tableColumn} key={product.id}>
<td className={styles.tableRow}>{product.id}</td>
<td className={styles.tableRow}>{product.name}</td>
<td className={styles.tableRow}>{product.price}</td>
</tr>
);
}
return null;
}
return null;
})}
</table>
</div>
);
}
export default Products;
Filter.jsx:
import CategoryComponent from "./Category";
import styles from "../styles/filter.module.css";
function Filter({
filters,
dispatch,
category,
getfilterList: filterList,
setFilterState: setFilterList,
range: rangeState,
}) {
const [Category, setCategory] = category;
const [Range, setRange] = rangeState;
const handleCategoryChanged = (event, categ, key) => {
let list = filterList();
if (list[categ][key].includes(event.currentTarget.value)) {
list[categ][key] = list[categ][key].filter(
(val) => val !== event.currentTarget.value
);
} else {
list[categ][key].push(event.currentTarget.value);
}
setFilterList(list);
if (Category !== categ) {
setCategory(categ);
} else {
let flag = false;
Object.keys(filterList()[categ]).forEach((key) => {
const obj = filterList()[categ][key];
if (obj.length !== 0) flag = true;
});
if (!flag) {
setCategory("all");
}
}
};
const handleRangeChanged = (event) => {
setRange(event.currentTarget.value);
};
return (
<div className="filter-container">
<h1>{Category}</h1>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
category={Category}
handleCategoryChanged={handleCategoryChanged}
categories={{
brand: ["lenovo", "msi", "asus"],
processor: ["intel core i3", "intel core i5", "intel core i7"],
}}
>
laptops
</CategoryComponent>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
handleCategoryChanged={handleCategoryChanged}
category={Category}
categories={{
brand: ["samsung", "infinx", "oppo"],
ram: ["8gb", "4gb", "2gb"],
storage: ["64gb", "128gb", "256gb"],
}}
>
phones
</CategoryComponent>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
category={Category}
handleCategoryChanged={handleCategoryChanged}
categories={{
type: ["keyboard", "mouse", "screen"],
}}
>
accessories
</CategoryComponent>
<div className={styles.priceRangeContainer}>
<input
type="range"
min="0"
max="9999"
className={styles.priceRange}
value={Range}
onChange={handleRangeChanged}
/>
<span>{Range}DT</span>
</div>
</div>
);
}
export default Filter;
Category.jsx
import React from "react";
import { useState, useEffect } from "react";
import styles from "../styles/category.module.css";
import arrow from "../img/angle-up-solid-svgrepo-com.svg";
export default function Category({
handleCategoryChanged,
children,
categories,
category,
filterList,
setFilterList,
}) {
const [hiddenClass, setHiddenClass] = useState(styles.hidden);
const handleClick = () => {
if (hiddenClass) setHiddenClass("");
else setHiddenClass(styles.hidden);
};
return (
<div>
<h5>{category}</h5>
<div onClick={handleClick} className={styles.categoryBox}>
{children} <img style={{ width: 15 }} src={arrow} alt="" />
</div>
<div className={styles.hiddenScroll + " " + hiddenClass}>
{Object.entries(categories).map(([key, value]) => {
return (
<div key={key}>
<h5>{key}:</h5>
<ul>
{value.map((val) => {
console.log(filterList);
return (
<li key={val}>
<input
type="checkbox"
name={val}
id={val}
value={val}
onChange={(e) =>
handleCategoryChanged(e, children, key)
}
/>
<label htmlFor={val}>{val}</label>
</li>
);
})}
</ul>
</div>
);
})}
</div>
</div>
);
}
The problem is when i run my program the state changes when i check an only one checkbox, but when i click on more checkboxes nothing changes.
EDIT:
my components hierarchy :
products.jsx
filter.jsx
Category.jsx
in products.jsx :
i have a state filterList that has all the filters i need.
const filter = {
laptops: {
brand: [],
processor: [],
},
phones: {
brand: [],
ram: [],
storage: [],
},
accessories: {
type: [],
},
};
const [filterList, setFilterList] = useState(filter);
in Filter.jsx:
i have state filterList that was passed as a prop from the Products component.
and a function that handles any checkbox change from it's child component.
const handleCategoryChanged = (event, categ, key) => {
let list = filterList();
//testing if the value is already in the filterList if true we remove it
if (list[categ][key].includes(event.currentTarget.value)) {
list[categ][key] = list[categ][key].filter(
(val) => val !== event.currentTarget.value
);
} else {
// else we add it to the list
list[categ][key].push(event.currentTarget.value);
}
setFilterList(list);
Category.jsx:
here i created all my checkboxes with the onChange event.
But the problem is when more than one checkbox gets changed, my filterList state does not get re rendered.
You missed the useState in your Filter.jsx
Replacing
const [Category, setCategory] = category;
const [Range, setRange] = rangeState;
With
const [Category, setCategory] = useState(category);
const [Range, setRange] = useState(rangeState);
A small working example could be found at code sandbox
⚠️ You did not provide the parent of Filter.jsx so in the example only the laptop brands are working
I am using react-select to store multiple elements and am using the map function to display elements which is working fine. But when I am using the same element in another class to display in a list element it shows a blank.
Here is the code where I am displaying the multiple options.
const Departments = [
{ label: "OneIT", value: "OneIT" },
{ label: "HR", value: "HR" },
{ label: "Vigilance", value: "Vigilance" },
{ label: "Ethics", value: "Ethics" },
{ label: "Corporate Services", value: "Corporate Services" },
{ label: "Legal", value: "Legal" },
{ label: "Sports", value: "Sports" },
{ label: "TQM", value: "TQM" },
{ label: "Iron Making", value: "Iron Making" },
{ label: "TMH", value: "TMH" }
];
class MultiSelect2 extends Component {
state = {
selectedOptions: []
};
handleChangeField = selectedOptions => {
this.setState({ selectedOptions });
};
render() {
const { selectedOption } = this.state;
return (
<div className="container">
<div className="row">
<div className="col-md-2"></div>
<div className="col-md-8">
<span>Select Department</span>
<Select
value={selectedOption}
options={Departments}
onChange={this.handleChangeField}
isMulti
/>
{this.state.selectedOptions.map(o => (
<p>{o.value}</p>
))}
</div>
<div className="col-md-4"></div>
</div>
</div>
);
}
}
I am trying to display this in another class in the list item but it is not showing.
export class Confirm extends Component {
state = {
selectedOptions: []
};
render() {
const {
values: {selectedOptions
}
} = this.props;
return (
<List>
<ListItemText primary="Departments" secondary={selectedOptions} />
</List>
I'm new to reactJs, I'm not sure where it went wrong.
I suppose there is something wrong with binding input. I suppose, cant change input because of value={detail.name}. However, even though I have deleted value={detail.name}, Name: {detail.name} still keeps the original value.
Could somebody give me a hint?
import React, { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
details: [
{ id: 1, name: "Tom", age: "20" },
{ id: 2, name: "zhunan", age: "22" },
{ id: 3, name: "kobe", age: "35" }
]
};
}
changeName(event) {
this.setState({
name: event.target.value
});
}
onDelete() {}
render() {
return (
<div>
<ul>
{this.state.details.map((detail, index) => (
<li key={index}>
Name: {detail.name} | age: {detail.age}
<input
style={{ marginLeft: "10px" }}
type="text"
onChange={this.changeName.bind(this)}
value={detail.name}
/>
</li>
))}
</ul>
</div>
);
}
}
export default App;
I updated the code a bit.
First of all, I moved the binding of the callback to the constructor (to have ONE callback instead of one per item*render)
I also changed the key used in the map to be the id, rather than the index of the current item.
Try, it, I hope it works for you.
import React, { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
details: [
{ id: 1, name: "Tom", age: "20" },
{ id: 2, name: "zhunan", age: "22" },
{ id: 3, name: "kobe", age: "35" }
]
};
this.changeName = this.changeName.bind(this);
}
changeName(event) {
const {target} = event;
const id = Number(target.dataset.id);
const { details } = this.state;
this.setState({
details: details.map((detail) => {
if (detail.id === id) {
return {
...detail,
name: target.value,
}
}
return detail;
}),
});
}
onDelete() {}
render() {
return (
<div>
<ul>
{this.state.details.map(({ id, age, name }) => (
<li key={id}>
Name: {name} | age: {age}
<input
style={{ marginLeft: "10px" }}
type="text"
onChange={this.changeName}
data-id={id}
value={name}
/>
</li>
))}
</ul>
</div>
);
}
}
export default App;
Your code works fine, nothing wrong with the input data binding. The problem is you're setting the name property directly to the state object. That would make it go from this:
this.state = {
details: [
{ id: 1, name: "Tom", age: "20" },
{ id: 2, name: "zhunan", age: "22" },
{ id: 3, name: "kobe", age: "35" }
]
}
To this:
this.state = {
details: [
{ id: 1, name: "Tom", age: "20" },
{ id: 2, name: "zhunan", age: "22" },
{ id: 3, name: "kobe", age: "35" }
],
name: "Bob"
}
Which has no effect on how the component gets rendered. To properly change the name of one of the details, which is what I assume you want, you also need to do a find that detail object to modify. Like this:
changeName(e, target_detail) {
this.setState({
// always update the WHOLE STATE OBJECT! using a map
details: this.state.details.map(detail => {
// the detail we want to modify has the same ID
if(detail.id === target_detail.id) {
// modify the name value of only that
target_detail.name = e.target.value
}
})
});
}
render method:
render() {
return (
<div>
<ul>
{this.state.details.map((detail, index) => (
<li key={index}>
Name: {detail.name} | age: {detail.age}
<input
style={{ marginLeft: "10px" }}
type="text"
// arrow functions implicitly "bind" the current this context:
onChange={e => this.changeName(e, detail)}
value={detail.name}
/>
</li>
))}
</ul>
</div>
);
}
I am having a list of data, which i displayed in the frontend using map function. Each data will have an switch button. If a user clicks on a particular, it will display an description regarding the data below. Now, if i use static data. it switch working properly. But all get selected at the same time. I want only the particular one to get select, when i click the particular one. I am using react-switch library for the switch. Below is my code, Pleasse check and let me know, how cal I achieve that.
/***Parent Component***/
import React, { Component } from "react";
import ReqLists from "./ReqLists";
class Requirements extends Component {
constructor(props) {
super(props);
this.state = {
reqs: [
{
id: 0,
name: "Application",
details: "Do you require an application from volunteers?"
},
{ id: 1, name: "Screening Questions", details: "", description: "Dummy content" },
{
id: 2,
name: "Recurring commitment",
details:
"Does this opportunity require a recurring commitment from volunteers?", description: "Dummy content"
},
{ id: 3, name: "Documents for volunteers to upload ", details: "", description: "Dummy content" },
{ id: 4, name: "Waiver & Release of Liability Forms", details: "", description: "Dummy content" },
{ id: 5, name: "Parental Consent & Medical Form", details: "", description: "Dummy content" },
{ id: 6, name: "Age", details: "", description: "Dummy content" },
{ id: 7, name: "Certifications", details: "", description: "Dummy content" },
{ id: 8, name: "Languages", details: "", description: "Dummy content" },
{ id: 9, name: "Skils", details: "", description: "Dummy content" },
{ id: 10, name: "Additional Requirements", details: "", description: "Dummy content" },
{ id: 11, name: "Additional Details", details: "", description: "Dummy content" }
],
checked: 0
};
}
handleChange = id => {
const checked = this.state.checked;
checked[id] = checked.hasOwnProperty(id) ? !checked[id] : true;
this.setState({ checked });
};
render() {
return (
<div style={{ width: "100%" }}>
<ReqLists
lists={this.state.reqs}
onChange={this.handleChange}
checked={this.state.checked}
/>
</div>
);
}
}
export default Requirements;
/***Child Component***/
import React, { Component } from "react";
import { withStyles, Typography } from "#material-ui/core";
import Switch from "react-switch";
class ReqLists extends Component {
render() {
const { lists, classes } = this.props;
return (
<div className={classes.reqWrapper}>
{lists &&
lists.map(list => {
return (
<div className={classes.reqCover} key={list.id}>
<div className={classes.reqDetails}>
<Typography className={classes.reqListName}>
{list.name}
<span className={classes.reqListDetails}>
{list.details}
</span>
</Typography>
<Switch
className={classes.reqSwitch}
onChange={() => this.props.onChange(list.id)}
checked={this.props.checked === list.id}
offColor="#cacaca"
onColor="#2299e9"
uncheckedIcon={
<div className={classes.checkedIcon}>NO</div>
}
checkedIcon={<div className={classes.checkedIcon}>YES</div>}
height={17}
width={35}
/>
</div>
{this.props.checked === list.id ? (
<div>
{list.description}
</div>
) : null}
</div>
);
})}
</div>
);
}
}
export default withStyles(styles)(ReqLists);
Change your handleChange method with this: All you need to check if the selected id is not equals to checked id update checked state.
handleChange = id => {
let selectedItemIndex = this.state.reqs.findIndex(item => item.id === id);
if (selectedItemIndex !== this.state.checked) {
this.setState({checked: selectedItemIndex});
}else{
this.setState({checked: null});
}
};
I created this mockup and I am trying to put this into real life
component mockup
I started from something simple so I created 3 buttons and an array
but what happens is that when I click on any button I see all of the features, and my goal was to when I click on SMS I see sms.features etc. But for now I see this that way current result
import React, { Component } from "react";
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
isHidden: true,
features: [
{
name: "sms.features",
key: "1",
icon: "sms icon"
},
{
name: "pricing.features",
key: "2",
icon: "pricing icon"
},
{
name: "api.features",
key: "3",
icon: "api icon"
}
],
buttons: [
{
name: "sms",
key: 1
},
{
name: "pricing",
key: 2
},
{
name: "api",
key: 3
}
]
};
this.toggleHidden = this.toggleHidden.bind(this);
}
toggleHidden() {
this.setState({
isHidden: !this.state.isHidden
});
}
render() {
return (
<div style={{ marginLeft: "20%" }}>
<div className="features__details__grid">
{!this.state.isHidden &&
this.state.features.map((object, key) => (
<div key={key}>{object.name}</div>
))}
</div>
<div className="buttons">
{this.state.buttons.map((button, key) => (
<div key={key}>
<button onClick={this.toggleHidden}>{button.name}</button>
</div>
))}
</div>
</div>
);
}
}
So, since you want to show only one, the isHidden should really be a pointer to which feature should be visible (by targeting the key property)
import React, { Component } from "react";
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
visibleFeature: "0",
features: [
{
name: "sms.features",
key: "1",
icon: "sms icon"
},
{
name: "pricing.features",
key: "2",
icon: "pricing icon"
},
{
name: "api.features",
key: "3",
icon: "api icon"
}
],
buttons: [
{
name: "sms",
key: "1"
},
{
name: "pricing",
key: "2"
},
{
name: "api",
key: "3"
}
]
};
this.toggleHidden = this.toggleHidden.bind(this);
}
toggleHidden(key) {
this.setState(state=>{
if (state.visibleFeature === key) return {visibleFeature: 0}
return {visibleFeature: key}
});
}
render() {
const feature = this.state.visibleFeature;
return (
<div style={{ marginLeft: "20%" }}>
<div className="features__details__grid">
{this.state.features.map((object) => (
feature === object.key && <div key={object.key}>{object.name}</div>
))}
</div>
<div className="buttons">
{this.state.buttons.map((button) => (
<div key={button.key}>
<button onClick={()=>this.toggleHidden(button.key)}>{button.name}</button>
</div>
))}
</div>
</div>
);
}
}
Demo at https://codesandbox.io/s/8y5q120wxj