embedded function in .map to access mapped information - javascript

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
});
});

Related

How to change this code more simply using for loop?

Please look at my code first.
I get 1 to 3 files from DB, depending on how many files did a writer uploaded. The maximum is 3, and if there's none, I render 'There is no file'.
To flexibly render files, I used three conditional statements. However, It is too messy and I think there is probably better idea than this.
const innerPost = () => {
const [files, setFiles] = useState([]);
const [deletedFilePk, setDeletedFilePk] = useState([]);
const deleteFile1 = () => {
setDeletedFilePk([...deletedFilePk, filePkNum]);
console.log(deletedFilePk);
};
const deleteFile2 = () => {
setDeletedFilePk([...deletedFilePk, filePkNum + 1]);
console.log(deletedFilePk);
};
const deleteFile3 = () => {
setDeletedFilePk([...deletedFilePk, filePkNum + 2]);
console.log(deletedFilePk);
};
return (
<>
{files.map((file) => {
if (file.length == 0) {
return (
<div>
<h5>THERE IS NO FILE.</h5>
</div>
);
}
if (file.length == 1) {
return (
<div>
<div>
<a
href={`/api/store/download-noticeboard-file fileId=${filePkNum}`}
>
{file[0]}
</a>
<Button
size='small'
onClick={deleteFile1}
>
delete
</Button>
</div>
</div>
);
}
if (file.length == 2) {
return (
<div>
<div>
<a
href={`/api/store/download-noticeboard-file?fileId=${filePkNum}`}
>
{file[0]}
</a>
<Button
size='small'
onClick={deleteFile1}
>
delete
</Button>
</div>
<div>
<a
href={`/api/store/download-noticeboard-file?fileId=${filePkNum + 1}`}
>
{file[1]}
</a>
<Button
size='small'
onClick={deleteFile2}
>
delete
</Button>
</div>
</div>
);
}
if (file.length == 3) {
return (
<div>
<div>
<a
href={`/api/store/download-noticeboard-file?fileId=${filePkNum}`}
>
{file[0]}
</a>
<Button
size='small'
onClick={deleteFile1}
>
delete
</Button>
</div>
<div>
<a
href={`/api/store/download-noticeboard-file?fileId=${filePkNum + 1}`}
>
{file[1]}
</a>
<Button
size='small'
onClick={deleteFile2}
>
delete
</Button>
</div>
<div>
<a href={`/api/store/download-noticeboard-file?fileId=${filePkNum + 2}`}></a>
<Button
size='small'
onClick={deleteFile3}
>
delete
</Button>
</div>
</div>
);
}
})}
</>
);
};
export default innerPost;
I need some wisdom!
You can map through the array and display each item and use the index property for your functions:
const innerPost = () => {
const [files, setFiles] = useState([]);
const [deletedFilePk, setDeletedFilePk] = useState([]);
const deleteFile = (index) => {
setDeletedFilePk([...deletedFilePk, index]);
console.log(deletedFilePk);
}
if (file.length == 0) {
return (
<div>
<h5>THERE IS NO FILE.</h5>
</div>
);
} else {
files.map((file, index) => {
return (
<div>
<a
href={`/api/store/download-noticeboard-file fileId=${index}`}
>
{file}
</a>
<Button
size='small'
onClick={() => deleteFile(index)}
>
delete
</Button>
</div>
);
})
}
};
export default innerPost;

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!

Change the displayed data dynamically in react-table v7

Not sure if there is a good way to change the displayed data dynamically. For instance I would like boolean values to be displayed in the table as icons and without slowing down the rendering process too much. I'm using the latest version of react-table v7. I have added code from the Table component Thanks
import React from "react";
import { useTable, useRowSelect, useSortBy, usePagination } from "react-table";
import { CSVLink, CSVDownload } from "react-csv";
import { navigate } from "#reach/router";
function handleColumnsClick(url, id) {
if (url && id) navigate(`${url}${id}`);
}
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
);
}
);
function Table({ columns, data, allowSelection, direct }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
// pagination
page, // Instead of using 'rows', we'll use page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { selectedRowIds, pageIndex, pageSize },
} = useTable(
{
columns,
data,
initialState: { pageIndex: 0 },
},
useSortBy,
usePagination,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
{allowSelection && (
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
)}
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
{allowSelection && (
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
)}
</div>
),
},
...columns,
]);
}
);
// Render the UI for your table
return (
<>
<table
className="table table-sm table-striped table table-hover"
{...getTableProps()}
>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th
className="checkbox"
{...column.getHeaderProps(column.getSortByToggleProps())}
>
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td
onClick={(e) =>
handleColumnsClick(
direct[1],
cell.row.original[direct[0]]
)
}
{...cell.getCellProps()}
>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<nav aria-label="Page navigation example">
<ul className="pagination justify-content-center">
<li className="page-item ">
<a
className="page-link"
tabIndex={-1}
aria-disabled="true"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
{"<< First Page"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
{"<"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => nextPage()}
disabled={!canNextPage}
>
{" >"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
{"Last Page >>"}
</a>
</li>
</ul>
<ul className="pagination justify-content-right">
<div className="pagination">
<span>
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</ul>
<p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
</nav>
<pre>
{/* Display selected rows for debugging */}
{/* <code>
{JSON.stringify(
{
selectedRowIds: selectedRowIds,
"selectedFlatRows[].original": selectedFlatRows.map(
(d) => d.original
),
},
null,
2
)}
</code> */}
</pre>
</>
);
}
function setColumns(props) {
const { data } = props;
if (data !== undefined && data.length !== 0) {
const columnsKeys = Object.keys(data[0]);
return columnsKeys.map((column) => {
return {
Header: column,
accessor: column,
};
});
}
}
function SelectTable(props) {
const columns = props.columns ? props.columns : setColumns(props);
const { data, allowSelection, direct } = props;
const addSelection = props.allowSelection ? props.allowSelection : false;
if (columns && data.length)
return (
<>
<Table
columns={columns}
data={data}
allowSelection={addSelection}
direct={direct}
/>
<button className="btn-outline btn-sm">
<CSVLink data={data}>Export to CSV </CSVLink>
</button>
</>
);
else return null;
}
export default SelectTable;

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