togglebutton failing in reactjs - javascript

I am trying to show different html body on same tab having two toggle buttons. I have two toggle buttons with one as pre-configured. I want to show different html on two different buttons, for doing this I have added one functions named getBody for rendering html.
But I'm not getting how to call this function with certain condition in toggle button. For doing this I have added one variable flag. And I am not getting how to use toggle button . It should be something like EbLog will be by default pre-configured and after that one can use CloudLog.
export default class ResourceLog extends React.Component {
constructor(props) {
super(props);
this.state = {
streamData: {},
streamMessage: "",
interval: 5000,
showDatePicker: false,
selectionConfigs: [
{
name: "1 Hr",
cssClass: "btn btn-info btn-sm",
duration: 300,
startTime: -1,
endTime: -1,
diff: 3600
},
{
name: "3 Hr",
cssClass: "btn btn-info btn-sm",
duration: 300,
startTime: -1,
endTime: -1,
diff: 10800
}
],
params: {},
show: "EbLog",
value: 1
};
}
streamLogs = () => {
if (this.props.resource.status === "ACTIVE") {
this.setState({ streamMessage: "loading logs.." });
axios.instance
.get("api/resources/" + this.props.resource.id + "/log/stream")
.then(resp => {
this.setState({ streamData: resp.data, streamMessage: "" });
})
.catch(err => {
this.setState({ streamMessage: "" });
NotificationManager.error("unable to fetch logs");
});
} else {
this.setState({
streamMessage: "Resource is not Active cannot fetch/stream logs..."
});
}
};
onCloud = () => {
let endTime = this.state.params.endTime;
if (endTime === -1) {
endTime = new Date().getTime();
}
let startTime = this.state.params.startTime;
if (startTime === -1) {
startTime = endTime - this.state.params.diff * 1000;
}
let filter = this.state.params.duration;
if (filter === -1) {
filter = "";
}
let params = Object.assign(
{},
{ startTime: startTime, endTime: endTime, filter: period }
);
this.setState({ streamMessage: "loading stats.." });
axios.instance
.get("api/resources/" + this.props.resource.id + "/cloud/log", {
params: params
})
.then(resp => {
this.setState({ data: resp.data, streamMessage: "" });
})
.catch(error => {
NotificationManager.error(`Failed to fetch stats-`, "Error");
});
};
render() {
return (
<div>
//<button style={edit} onClick={this.streamLogs}>
EbLog
</button>
//<button style={edit} onClick={this.onCloud}>
CloudLog
</button>
<ButtonToolbar>
<ToggleButtonGroup type="checkbox" defaultValue={[1]}>
<ToggleButton value={1}>EBLog</ToggleButton>
{}
<ToggleButton value={2}>CloudLog</ToggleButton>
</ToggleButtonGroup>
</ButtonToolbar>
{this.getBody()}
</div>
);
}
getBody = () => {
if (this.state.show == "EbLog") {
return (
<div className="row" style={{ margin: "15px" }}>
<div className="col-md-12">
<h3 style={{ display: "inline" }}>
Logs
<span
style={{ marginLeft: "10px", cursor: "pointer" }}
title={"Download"}
>
<a
target="_blank"
href={`api/resources/${this.props.resource.id}/log`}
>
<i className="fa fa-download" />
</a>
</span>
<span
style={{ marginLeft: "10px", cursor: "pointer" }}
title={"Refresh"}
>
<i onClick={this.streamLogs} className="fa fa-refresh" />
</span>
</h3>
</div>
<div className="clearfix" />
<br />
<div className="col-md-12">
<div>{this.state.streamMessage}</div>
{mapObject(this.state.streamData, (k, v) => (
<div>
<pre>{v.join("\n")}</pre>
</div>
))}
</div>
</div>
);
} else if (this.state.show == "CloudLog") {
return (
<div className="row" style={{ margin: "20px", textAlign: "center" }}>
<div>
<span
className={this.state.selectionConfigs[0].cssClass}
onClick={() =>
this.updateStatsConf(this.state.selectionConfigs[0])
}
>
{this.state.selectionConfigs[0].name}
</span>
<span
style={{ marginLeft: "10px" }}
className={this.state.selectionConfigs[1].cssClass}
onClick={() =>
this.updateStatsConf(this.state.selectionConfigs[1])
}
>
{this.state.selectionConfigs[1].name}
</span>
<span
style={{ marginLeft: "10px" }}
className={this.state.selectionConfigs[2].cssClass}
onClick={() =>
this.updateStatsConf(this.state.selectionConfigs[2])
}
>
{this.state.selectionConfigs[2].name}
</span>
</div>
<div className="row" style={{ margin: "20px" }}>
<div className="col-md-12">
<div>{this.state.streamMessage}</div>
{mapObject(this.state.streamData, (k, v) => (
<div>
<pre>{v.join("\n")}</pre>
</div>
))}
</div>
</div>
</div>
);
} else {
<h1> please click a Button </h1>;
}
};
}
Please refer this link. I have used this link as reference for creating ToggleButton under section Controlled

From what I understand, you need to create a simple method to change state on click. Say something like;
toggleButton = (text) => {
this.setState({show: text});
}
And then use onClick attribute in your ToggleButton element;
<ToggleButtonGroup type="checkbox" defaultValue={[1]}>
<ToggleButton value={1} onClick={this.toggleButton.bind(this, "EbLog")}>EBLog</ToggleButton>
{}
<ToggleButton value={2} onClick={this.toggleButton.bind(this, "CloudLog")}>CloudLog</ToggleButton>
</ToggleButtonGroup>

From what i understood from the problem, please refer the following example to render different views according to toggle button click.
The toggle button should be as follows
<ToggleButton onClick = {this.toggleButton}/>
The method should update the state
toggleButton(value){
value ? this.setState(show:'CloudLog') : this.setState(show:'EbLog')
}
The render method can be as
render() {
return(
this.state.show ==='CloudLog' ? <div>View 1</div> : <div>View 2</div>
)
}

Related

Set timeout on material UI drawer when open

I would like to close the drawer after a specific time however I dont think material UI drawer has that kind of props. Is there a way to use transitionDuration to close the drawer or maybe add a setTimeout in my function ? When adding a timeout in my toggleDrawer function, it won't notice the timeout.
<Drawer
className="draww"
anchor={anchor}
open={state[anchor]}
transitionDuration={2000}
onClose={toggleDrawer(anchor, false)}
>
<div className="drawer-title">
<Link to="/">
<h2>NOW NOW</h2>
</Link>
</div>
<a className="cl" onClick={toggleDrawer(anchor, false)}>
×
</a>
{/* {list(anchor)} */}
<br />
<div className="cart-drawer">
<h4>YOUR SELECTION</h4>
<div className="border-cart"></div>
{cartItems.map((data) => (
<div className="row thiss">
<Link
to={{ pathname: `/product/${data._id}` }}
onClick={toggleDrawer(anchor, false)}
>
{data.product_image && (
<img
className="drawer-pic"
src={data.product_image.secure_url}
alt=""
/>
)}
</Link>
<div className="col info-vart">
<Link
to={{ pathname: `/product/${data._id}` }}
onClick={toggleDrawer(anchor, false)}
>
<h2>{data.product_name.slice(0, 12)}</h2>
<h5>Ref:{data.product_details[1].TAILLE}</h5>
<button
onClick={() => removeProduct(data)}
className="remove"
>
DELETE
</button>
</Link>
</div>
<h3>€{data.product_price}</h3>
</div>
))}
<div className="border-cart"></div>
<div className="draw-down">
<div className="row ">
<p className="sub">TOTAL</p>
<p className="sub total">{formatNumber(total)}</p>
</div>
<div className="centerthis">
<button
type="button"
onClick={someFunc}
className="checkoutt"
role="link"
>
CHECKOUT
</button>
<PayPalScriptProvider
options={{
"client-id":
"",
currency: "EUR",
}}
>
<div
className="paypalll"
style={{
minWidth: "280px",
maxWidth: "280px",
}}
>
<PayPalButtons
style={{
layout: "horizontal",
height: 45,
}}
createOrder={(data, actions) => {
return actions.order.create({
purchase_units: [
{
amount: {
value: total,
},
},
],
});
}}
/>
</div>
</PayPalScriptProvider>
</div>
</div>
<br />
</div>
<br />
</Drawer>
Toggle drawer function
const toggleDrawer = (anchor, open, data) => (event) => {
if (
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return addProduct(data);
}
setState({ ...state, [anchor]: open });
};
You can use a useEffect hook to close drawer after a timeout.
const allAnchors = ['left', 'right', 'top', 'bottom'];
for(let anchor of allAnchors) {
useEffect(() => {
if (state[anchor]) {
setTimeout(() => {
toggleDrawer(anchor, false)()
}, 2000); // close after 2000ms
}
}, [state[anchor]]);
}
I saw you're using anchor variable, hinting that you might have multiple directions for drawer.
If that's not the case, you can modify code and remove the for-loop entirely.
Here's a working example
const { useState, useEffect } = React;
const { Drawer, Button } = MaterialUI;
const App = (props) => {
const [ state, setState ] = useState({});
const allAnchors = [ 'left', 'right', 'top', 'bottom' ];
for(let anchor of allAnchors) {
useEffect(() => {
if (state[anchor]) {
setTimeout(() => {
toggleDrawer(anchor, false)()
}, 2000); // close after 2000ms
}
}, [state[anchor]]);
}
const toggleDrawer = (anchor, open, data) => (event) => {
if (
// check if event is not undefined
event &&
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return addProduct(data);
}
if( typeof open === 'undefined') open = !state[anchor]
setState(s => ({ ...s, [anchor]: open }));
};
return (
<div>
{ allAnchors.map(anchor =>
(
<React.Fragment>
<Button onClick={toggleDrawer(anchor)} >
{anchor}
</Button>
<Drawer
anchor={anchor}
open={state[anchor]}
onClose={toggleDrawer(anchor, false)}
>
This is Drawer
</Drawer>
</React.Fragment>
))
}
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/#material-ui/core#4.12.1/umd/material-ui.development.js"></script>
<div id="root" />

How to access index of .map() method outside of it in react

I want to access the index number outside of that map method because I want to use that index conditionally to show other component like if that index is checked then the component will show but I can't figure out how to access that outside of that map method. I have googled it but couldn't find any proper solution of that.
Here is my code!
import React, { Component, Fragment } from "react";
import Clear from "./clear";
import Display from "./display";
import NoOfItems from "./noOfItems";
class MainPage extends Component {
constructor() {
super();
this.state = {
data: [
{
name: "",
completed: false,
},
],
data: [],
checkValue: false,
};
}
handleChange = (e) => {
e.preventDefault();
this.setState({ name: e.target.value });
};
handleSubmit = (e) => {
e.preventDefault();
this.setState({
data: [...this.state.data, { name: this.state.name, completed: false }],
});
e.target.reset();
};
handleDelete = (index) => {
const newList = [...this.state.data];
newList.splice(index, 1);
this.setState({ data: newList });
};
handleAllCheck = (e) => {
e.preventDefault();
};
handleCheckChange = (index) => {
let newData = [...this.state.data];
newData[index].completed = !newData[index].completed;
this.setState({ data: newData });
};
render() {
return (
<Fragment>
<h1 className="display-1 text-center" style={{ color: "#f7c6c6" }}>
todos
</h1>
<form className="todo-form" onSubmit={this.handleSubmit}>
<label className="label" onClick={this.handleAllCheck}>
^
</label>
<input
autoFocus
type="text"
onChange={this.handleChange}
className="new-todo shadow-lg p-3 mb-5 bg-white"
placeholder="What needs to be done?"
/>
<ul className="list-group">
{this.state.data.map((data, index) => {
return (
<div key={"todo-" + index} className="div-list">
<input
className="check"
onChange={() => this.handleCheckChange(index)}
type="checkbox"
style={{
cursor: "pointer",
}}
defaultChecked={this.state.data.completed}
/>
<li
className="list-group-item disabled w-50 p-3 mx-auto"
style={{
textDecoration:
this.state.data[index].completed && "line-through",
}}
>
{data.name}
</li>
<button
onClick={() => this.handleDelete(index)}
type="button"
className="close"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
);
})}
</ul>
{this.state.data.length > 0 && <Display />}
{this.state.data.length > 0 && (
<NoOfItems noOfTodos={this.state.data.length} />
)}
{this.state.data.completed && <Clear />}
</form>
</Fragment>
);
}
}
export default MainPage;
Just ignore the rest and see the map method and its index. At the end of the code I have used this.state.data.completed at this point I want to use the index like this this.state.data[index].completed but its saying invalid declaration of Index. Please help me I am stuck!
Thank you in advance!

embedded function in .map to access mapped information

I am using a .map function to display details to user.
I would like a button so that when it is clicked it expands for the user displaying more in depth information.
As it is mapped I cannot set a function as I would normally (between constructor and render)as it would not understand the mapped information e.g r.AssignedWorkStation
essentially I am asking is it possible to put a function within here (example below) and then have this be able to access the mapped properties
const renderTodos = currentTodos.map(r => {
Test(){
if(me){
//function to do what I want
}
}
return (
<>
<div className="jumbotron">
<button className="btn btn-primary" style={{ float: "right" }}>
View Details
</button>
<br />
<li>
<b>Workstation : </b>
{r.AssignedWorkStation}
</li>
<li>
<b>Date: </b>
{r.Date}
</li>
<li>
<b>Status: </b>
{r.CompleteToken}
</li>
<br />
</div>
</>
);
});
Whole class code
var results = [];
class AdminWorkstations extends React.Component {
constructor() {
super();
this.state = {
questions: [],
viewDetails: false,
currentPage: 1,
todosPerPage: 4
};
this.getQuestionByUniqueDate = this.getQuestionByUniqueDate.bind(this);
// this.test = this.test.bind(this);
}
// sets the questions form sql into state for questions
handleClick = event => {
this.setState({
currentPage: Number(event.target.id)
});
};
// test() {
// alert(r.AssignedWorkStation);
// }
componentDidMount() {
fetch(`/admin-completed-workstations`)
.then(recordset => recordset.json())
.then(results => {
this.setState({ questions: results.recordset });
console.log(this.state.questions);
this.state.questions &&
this.getQuestionByUniqueDate(this.state.questions);
});
}
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
}
getQuestionByUniqueDate(questions) {
for (var i = 0; i < questions.length; i++) {
if (
!results.find(q => q.Date == questions[i].Date) ||
!results.find(
q => q.AssignedWorkStation == questions[i].AssignedWorkStation
)
) {
results.push(questions[i]);
this.setState({ amountOfWorkstations: results.length });
}
}
return results;
}
render() {
const { currentPage, todosPerPage } = this.state;
// Logic for displaying current todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo);
debugger;
const renderTodos = currentTodos.map(r => {
return (
<>
<div className="jumbotron">
<button className="btn btn-primary" style={{ float: "right" }}>
View Details
</button>
<br />
<li>
<b>Workstation : </b>
{r.AssignedWorkStation}
</li>
<li>
<b>Date: </b>
{r.Date}
</li>
<li>
<b>Status: </b>
{r.CompleteToken}
</li>
<br />
{/* <Questions results={r}></Questions> */}
</div>
</>
);
});
const pageNumbers = [];
for (
let i = 1;
i <= Math.ceil(this.state.amountOfWorkstations / todosPerPage);
i++
) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<button
className="btn btn-primary"
key={number}
id={number}
onClick={this.handleClick}
>
{number}
</button>
);
});
let selectedWorkStation = window.localStorage.getItem("Workstation");
console.log(this.state.questions);
if (this.state.questions.length) {
return (
<div>
<h2 style={{ textAlign: "center" }}>
Completed Workstation Assessments
</h2>
<ul>
<button disabled className="btn btn-secondary">
Workstation Assessments
</button>
<Link to="./admin-center">
<button className="btn btn-secondary">Edit Questions</button>
</Link>
<Link to="./admin-center-view-users">
<button className="btn btn-secondary">View Users</button>
</Link>
<DropdownButton
style={{ float: "right" }}
id="dropdown-basic-button"
title="Completed"
>
<Dropdown.Item>
{" "}
<Link to="admin-view-workstation-assessments-declined">
In Progress
</Link>
</Dropdown.Item>
</DropdownButton>{" "}
</ul>
<ul>
{renderTodos}{" "}
<div
style={{ userSelect: "none", cursor: "pointer" }}
id="page-numbers"
>
{renderPageNumbers}
</div>
</ul>
</div>
);
} else if (!this.state.questions.length) {
return (
<>
{" "}
<div>
<h3 style={{ textAlign: "center" }}></h3>
<ul>
<br />
<br />{" "}
<div>
<h6>
{" "}
<tr>
Desk Location Selected :{" "}
<u style={{ color: "grey" }}>{selectedWorkStation}</u>
</tr>
</h6>
</div>
<div className="jumbotron">
<li style={{ textAlign: "center" }}>
<b>no completed Workstation Self-Assessments</b>{" "}
</li>
</div>
</ul>
</div>
</>
);
}
}
}
You should save your todos inside your component state, not compute it inside render.
You shouldn't have a global variable called results either, store that inside your component state as well.
Here is a small example:
fetch(`/admin-completed-workstations`)
.then(recordset => recordset.json())
.then(results => {
this.setState({ questions: results.recordset });
console.log(this.state.questions);
// Here, inside getQuestionByUniqueDate you should store result using this.setState instead of having a global variable
// Then you can simply move the entire renderTodo function outside the render function of this component
this.state.questions &&
this.getQuestionByUniqueDate(this.state.questions);
});
LE: Here is a comprehensive article about fetching data in React.js apps:
https://www.robinwieruch.de/react-fetching-data (I recommend reading it)
LE2: You can assign both results and todos inside your componentDidMount
getQuestionByUniqueDate(questions) {
const currentResults = this.state.results ? [...this.state.results] : [];
for (var i = 0; i < questions.length; i++) {
if (
!currentResults.find(q => q.Date == questions[i].Date) ||
!currentResults.find(
q => q.AssignedWorkStation == questions[i].AssignedWorkStation
)
) {
currentResults.push(questions[i]);
}
}
return currentResults;
}
fetch(`/admin-completed-workstations`)
.then(recordset => recordset.json())
.then(res => {
const results = res.recordset &&
this.getQuestionByUniqueDate(res.recordset);
// Logic for displaying current todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo);
this.setState({
questions: res.recordset,
results,
currentTodos,
amountOfWorkstations: results.length
});
});

Multiple fields appearing under a parent in ReactJs

class CriteriaSetValue extends Component {
state = {
count: 0,
tableName: "",
fieldName:"",
tables: [],
fields: [],
addLine: [],
addField: []
};
onSubmit = e => {
e.preventDefault();
const addField = this.state.addField;
const size = addField.length + 1;
addField.push(size);
this.setState({
addField
});
addNewLine = event => {
event.preventDefault();
const addLine = this.state.addLine;
const size = addLine.length + 1;
const nextLine = this.state.count + 1;
addLine.push(size);
this.setState({
count: nextLine,
addLine
});
};
render() {
const {showForm, tableName, fieldName } = this.state;
const { props, state } = this;
return (
<React.Fragment>
<div className="form-wrapper">
<div className="row">
<div className="col-10 container">
<form onSubmit={this.submit} className="card">
<div className="card-header">
<h3 className="card-title">
Criteria Set
{/* <Locale value="std.formupload" /> */}
</h3>
</div>
<div className="card-body">
<div className="row">
<div className="col-md-7 col-lg-8">
<div className="add-line">
<Button
icon
labelPosition="left"
onClick={this.addNewLine}
>
Add Line
<Icon name="plus" />
</Button>
{this.state.addLine.map(index => {
return (
<div
className="field-line"
style={{ marginTop: "30px" }}
key={index}
id={index}
>
<h4 className="field-button">Line {index}</h4>
<Button
className="field-button"
icon
onClick={this.toggleForm}
>
<Icon name="box" />
</Button>
</div>
);
})
}
{
this.state.addField.map(index => {
return (
<React.Fragment>
<div
className="field-button"
style={{
marginTop: "20px",
paddingLeft: "20px"
}}
key={index}
id={index}
>
<h4
className="field-button"
onclick={this.addCriteriaValue}
>
<span>
table.field
</span>
</h4>
<Button
className="field-button"
icon
onClick={this.toggleDelete}
>
<Icon name="delete calendar" />
</Button>
</div>
<br></br>
</React.Fragment>
);
})
}
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
);
}
};
This is kind of related to what I am trying to achieve:
https://codesandbox.io/s/3vqyo8xlx5
But I want the child to appear under the preference of where the "Add child" button is clicked, not on the last one.
I am working on making multiple fields to appear under each line when a button beside line1, line2 and so on is clicked, but currently, the field jump to last lines when a button is clicked, it is not appearing on the appropriate line.
I have been able to show lines when the "Add Line" button is clicked and I have also been able to show the field when the button beside "Line #1" is clicked.
I want the fields for "Line #1" to show under the line1 when the field button is clicked, the field for "Line #2" to show under the line2 when the field button beside it is clicked and so on
You can try something like this:
const AddButton = ({ label, onClick }) => (
<button onClick={onClick}>
{label} [+]
</button>
)
const FieldData = ({ label, criterias, onAdd, onDelete }) => (
<React.Fragment>
<div
className='field-button'
style={{
marginTop: '20px',
paddingLeft: '20px'
}}
>
<h4 className='field-button'>
<AddButton
label='Add criteria'
onClick={onAdd}
/>
<span>
{label}
</span>
</h4>
{criterias.map((item, idx) => (
<p key={idx}>{item.id}</p>
))}
<button
className='field-button'
onClick={onDelete}
>
Del [-]
</button>
</div>
<br />
</React.Fragment>
)
class App extends React.PureComponent {
state = {
lines: []
}
handleSubmit = e => {
e.preventDefault()
console.log('DATA TO SAVE ' + JSON.stringify(this.state.lines))
}
handleAddLine = event => {
event.preventDefault()
this.setState(prevState => ({
...prevState,
lines: [
...prevState.lines,
{
id: (prevState.lines.length + 1),
fields: []
}
]
}))
}
handleAddField = lineId => e => {
e.preventDefault()
this.setState(prevState => {
const newLines = [ ...prevState.lines ]
const curLine = newLines[newLines.findIndex(({ id }) => id === lineId)]
curLine.fields.push({
id: curLine.fields.length + 1,
criterias: []
})
return {
...prevState,
lines: newLines
}
})
}
handleAddCriteria = (lineId, fieldId) => event => {
event.preventDefault()
this.setState(prevState => {
const newLines = [ ...prevState.lines ]
const curLine = newLines[newLines.findIndex(({ id }) => id === lineId)]
const curField = curLine.fields[curLine.fields.findIndex(({ id }) => id === fieldId)]
curField.criterias.push({
id: curField.criterias.length + 1
})
return {
...prevState,
lines: newLines
}
})
}
handleDeleteField = (lineId, fieldId) => event => {
event.preventDefault()
this.setState(prevState => {
const newLines = [ ...prevState.lines ]
const curLine = newLines[newLines.findIndex(({ id }) => id === lineId)]
curLine.fields = curLine.fields.filter(item => item.id !== fieldId)
return {
...prevState,
lines: newLines
}
})
}
render() {
const { lines } = this.state
return (
<React.Fragment>
<div className='form-wrapper'>
<div className='row'>
<div className='col-10 container'>
<form
onSubmit={this.handleSubmit}
className='card'
>
<div className='card-header'>
<h3 className='card-title'>
Criteria Set
</h3>
</div>
<div className='card-body'>
<div className='row'>
<div className='col-md-7 col-lg-8'>
<div className='add-line'>
<AddButton
label='Add Line'
onClick={this.handleAddLine}
/>
{lines.map((line, idx) => {
return (
<React.Fragment key={idx}>
<div
className='field-line'
style={{ marginTop: '30px' }}
>
<h4 className='field-button'>
Line {idx+1}
</h4>
<AddButton
label='Add field'
onClick={this.handleAddField(line.id)}
/>
</div>
{line.fields.map((lField, idx) => (
<FieldData
label={idx+1}
criterias={lField.criterias}
onAdd={this.handleAddCriteria(line.id, lField.id)}
onDelete={this.handleDeleteField(line.id, lField.id)}
/>
))}
</React.Fragment>
)
})}
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I defined a new data structure for the state, so It is better to think about nested data, and I refactored the code a little.

Toggle display specific element inside map function onClick

I have a plus sign which onClick switches to Minus sign. With that, a list of grades is also displayed based on toggle.
Currently when I click on the plug sign, all the elements expand displaying list for all students. I want to only toggle the list for the specific student.
The data is being pulled from an API then being displayed.
class Home extends Component {
constructor(props) {
super(props);
this.state = {
students: [],
search: "",
display: false
};
}
_iconOnClick = event => {
this.state.display ? this.setState({ display: false }) : this.setState({ display: true })
;
};
_renderDataFromAPI = () => {
//search filter
let filteredName = this.state.students.filter(student => {
return (
student.firstName
.toLowerCase()
.includes(this.state.search.toLowerCase()) ||
student.lastName.toLowerCase().includes(this.state.search.toLowerCase())
);
});
return filteredName.map((student,i) => {
let average =
student.grades.reduce((x, y) => parseInt(x) + parseInt(y)) /
student.grades.length;
return (
<div key={i}>
<div className="data">
<img className="img" src={student.pic} align="left" />
{this.state.display ? (
<FontAwesomeIcon
style={{
float: "right",
fontSize: "30px",
marginRight: "40px"
}}
icon="minus"
onClick={this._iconOnClick}
/>
) : (
<FontAwesomeIcon
style={{
float: "right",
fontSize: "30px",
marginRight: "40px"
}}
icon="plus"
onClick={this._iconOnClick}
/>
)}
<h1>
{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {average}%</p>
<br />
{this.state.display ? (
<div >
{student.grades.map((grade, i) => {
return (
<p key={i}>
Test {i + 1}: {grade}%
</p>
);
})}
</div>
) : (
<div />
)}
</div>
<hr style={{ borderColor: "#e2e1e1" }} />
</div>
);
});
};
Move the display to each student.
let dataFromAPI = getDataFromAPI();
this.setState({
students: dataFromAPI.map(
student => Object.assign(student, { display: false })
});
Display the list...
{student.display ? (
<div >
{student.grades.map((grade, i) => { ...
Toggle the show/hide
_iconOnClick = id => {
let students = this.students.map(s => {
if (id == s.id) {
return Object.assign(s, { display: !s.display });
}
return s;
});
this.setState({ students });
};
...
<FontAwesomeIcon
style={{
float: "right",
fontSize: "30px",
marginRight: "40px"
}}
icon="minus"
onClick={this._iconOnClick(student.id)} />

Categories

Resources