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});
}
};
Related
I'm trying to create a Drag and Drop component in which you can drag and drop each component to re-sort them.
For example, consider the following array
const myData = [
{ id: 0, text: "test one" },
{ id: 1, text: "test tow" },
{ id: 2, text: "test three" },
{
id: 3,
text: "main",
sub: [
{ id: 4, text: "sub 1" },
{ id: 5, text: "sub 2" },
],
},
];
if the user drag the element (component) with the text "test one" and drop it under the element { id: 4, text: "sub 1" },
I want to reorder the data again to be like the following
const myData = [
{ id: 1, text: "test tow" },
{ id: 2, text: "test three" },
{
id: 3,
text: "main",
sub: [
{ id: 4, text: "sub 1" },
{ id: 0, text: "test one" },
{ id: 5, text: "sub 2" },
],
},
];
this the component that renders the data.
import Draggable from "../Hooks/Draggable";
function TreeItems(state) {
const [state, dispatch] = useReducer(reducer, myData);
return state.map((item, index) => (
<TreeItem
key={index}
nodeId={`${index}`}
label={
<div draggable={true} {...Draggable(item, index, Update)}>
{item.text}
</div>
}
>
{item.sub && TreeItems(item.sub)}
</TreeItem>
));
}
I tried to delete the dragged element and then add it again in the child or parent array where the drop event occurs.
function deleting(state, action) {
state.forEach((item) => {
if (item.id === action.i.item.id) {
state.splice(action.i.index, 1);
} else if (item.sub) {
deleting(item.sub, action);
}
});
return state;
}
function adding(state, action) {
state.forEach((item) => {
if (item.id === action.item.id) {
state.push(action.i.item);
//max call stack size.
} else if (item.sub) {
adding(item.sub, action);
}
});
return state;
}
function reordering(state, action) {
// const { type, i, f, p } = action;
const newState = deleting(state, action);
const newState2 = adding(newState, action);
return newState2;
}
function reducer(state, action) {
switch (action.type) {
case ACTIONS.REORDER:
const newState = reordering(state, action);
return [...newState];
case ACTIONS.SUB:
return state;
case ACTIONS.UPSATETEXT:
return state;
default:
console.log("none");
}
}
but I got errors like
max calls exeated
and I change the code in too many ways the deleting function work very fine but the adding function cause errors like max calls or I remember I change it in some way and it duplicate the drag-drop element instead of deleting add and then add it.
CodeSandBox code
note the sandBox says can't import material-ui dependencies and I have no idea why but it works very fine on my local machine.
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;
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 trying to create component in react. I am on learning mode. So may be I am doing totally wrong. Below is my code
import * as React from 'react'
import styles from './MegaMenu.module.scss'
interface IMenu {
name: string
link: string
subitem?: IMenu[]
}
let menus: IMenu[]
menus = [
{
name: "Home",
link: "#"
},
{
name: "About Us",
link: "#"
},
{
name: "Products",
link: "#",
subitem: [
{
name: "SubItem 1",
link: "#",
subitem: [
{
name: "Sub-SubItem1",
link: "#"
},
{
name: "Sub-SubItem2",
link: "#"
}
]
},
{
name: "SubItem 2",
link: "#"
}
]
},
{
name: "Services",
link: "#",
subitem: [
{
name: "SubItem 1",
link: "#"
},
{
name: "SubItem 2",
link: "#"
}
]
}
]
class MegaMenu extends React.Component {
public render() {
return (
<div className={styles.MegaMenu}>
<div className={styles["menu-container"]}>
<div className={styles.menu}>
<MenuList Options={menus} />
</div>
</div>
</div>
)
}
}
const MenuList = (Options: IMenu[]) => {
return (
<ul>
{
Options.map((Option: IMenu) => (
<li key="">
<a href={Option.link}>{Option.name}</a>
{/* Base Case */}
{
(Option.subitem && Option.subitem.length > 0) &&
<MenuList Options={Option.subitem} />
}
</li>
))
}
</ul>
)
}
export default MegaMenu
I get below error
Kindly somebody help me
Functional components can only have props as variable, therefore you have to provide a typing for props and use it like this.
interface IMenuListProps {
options: IMenu[]
}
const MenuList = (props: IMenuListProps) => {
return (
<ul>
{
props.options.map((Option: IMenu) => (
<li key="">
<a href={Option.link}>{Option.name}</a>
{/* Base Case */}
{
(Option.subitem && Option.subitem.length > 0) &&
<MenuList options={Option.subitem} />
}
</li>
))
}
</ul>
)
}
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