How to destructure an array of objects while function passing in javascript - javascript

I was trying to rewrite the todo app using functional Components and I could not figure out a way to destructure and array of objects. Appreciate any help.
The Original Component:
class ProductTable extends React.Component {
render() {
let rows = [];
let lastCategory = null;
this.props.products.forEach(function(product) {
if (product.category !== lastCategory) {
rows.push(<ProductCategoryRow category={product.category} key={product.category} />);
}
rows.push(<ProductRow product={product} key={product.name} />);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
The Functional Component I am trying to write:
const ProductTable = ({products = []}) => {
let rows = [];
let lastCategory = null;
products.forEach(function (product) {
if (product.category !== lastCategory) {
rows.push(<ProductCategoryRow category={product.category} key={product.category} />);
}
rows.push(<ProductRow product={product} key={product.name} />);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
};
Can the FormalParameter be more restrictive?
const ProductTable = ({products = []}) => ...

Related

Search in custom table - React

I'm trying to make a table with api data searchable. I'm on my way, but unsure on how to proceed further.
Code looks like this:
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
const handleChange = event => {
setSearchTerm(event.target.value);
};
useEffect(() => {
const results = apiData.filter(person =>
person.toLowerCase().includes(searchTerm)
);
setSearchResults(results);
}, [searchTerm]);
const renderPerson = (contact, index) => {
return (
<tr key={index}>
<td>{contact.ID}</td>
<td>{contact.Info.Name}</td>
<td>{contact.InfoID}</td>
<td>{contact.Info.DefaultEmail.EmailAddress}</td>
<td>{contact.Info.DefaultPhone.Number}</td>
<td>{contact.Info.InvoiceAddress.AddressLine1}</td>
</tr>
)
}
return (
<Fragment>
<input type="text" value={searchTerm} onChange={handleChange} placeholder="Søk.."></input>
<table id="myTable">
<thead>
<tr className="header">
<th>ID</th>
<th>Navn</th>
<th>Info Id</th>
<th>E-post</th>
<th>Telefon</th>
<th>Adresse</th>
</tr>
</thead>
<tbody>
{apiData.map(renderPerson)}
</tbody>
</table>
</Fragment>
)
https://dev.to/asimdahall/simple-search-form-in-react-using-hooks-42pg
I've followed this guide, but since I have the renderPerson function, I'm a bit unsure on how to handle this.
Question: Is there any way to get this working, or am I approaching it the wrong way? I'm aware that I need to put searchResult in the tbody somehow, but then the table won't be populated.
Any help is much appreciated
Edit: displaying code for getting apiData:
useEffect(() => {
getContacts()
}, [])
const getContacts = () => {
$.ajax({
url: `http://localhost:5000/getContacts`,
type: "POST",
data: ajaxObj,
success: (data) => {
let response = JSON.parse(data)
setApiData(response)
setLoading(false)
},
error: () => {
console.log("noe feilet?");
}
});
}
console.log(apiData)
Data looks like this:
data
Change apiData to searchResults on render
<tbody>
{searchResults.map(renderPerson)}
</tbody>
Change your filter way result (Updated)
const results = apiData.filter(person =>
person.Info.Name.toLowerCase().includes(searchTerm)
);
....
const renderPerson = (item, index) => {
return (
<tr key={index}>
<td>{item.ID}</td>
<td>{item.Info.Name}</td>
<td>{item.InfoID}</td>
<td>{item.Info.DefaultEmail.EmailAddress}</td>
<td>{item.Info.DefaultPhone.Number}</td>
<td>{item.Info.InvoiceAddress.AddressLine1}</td>
</tr>
)
}
Try this
export default function () {
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
const apiData=[
{
ID:'1222',
Info:{
ID:'1222',
EmailAddress:'test#test.com',
Number:'2222222222',
AddressLine1:'test'
}
},
{
ID:'2333',
Info:{
ID:'2333',
EmailAddress:'test2#test.com',
Number:'1111111111',
AddressLine1:'test2'
}
}
]
const handleChange = event => {
setSearchTerm(event.target.value);
if(event.target.value){
const results = apiData.filter(person =>
person.ID.toLowerCase().includes(event.target.value)
);
setSearchResults(results);
}else{
setSearchResults([]);
}
};
const renderPerson = (contact, index) => {
return (
<tr key={index}>
<td>{contact.ID}</td>
<td>{contact.Info.Name}</td>
<td>{contact.Info.ID}</td>
<td>{contact.Info.EmailAddress}</td>
<td>{contact.Info.Number}</td>
<td>{contact.Info.AddressLine1}</td>
</tr>
)
}
return (
<Fragment>
<input type="text" value={searchTerm} onChange={handleChange} placeholder="Søk.."></input>
<table id="myTable">
<thead>
<tr className="header">
<th>ID</th>
<th>Navn</th>
<th>Info Id</th>
<th>E-post</th>
<th>Telefon</th>
<th>Adresse</th>
</tr>
</thead>
<tbody>
{searchResults.map(renderPerson)}
</tbody>
</table>
</Fragment>
)
}

Sorting Html table Columns reactjs

i am trying to sort html table by ASC and desc order but this table is not working properly its working only for first column when i put id name can you please help me for make this work sorting func by ASC and Desc. this is my code so far i tried but its not working Thanks
import React from "react";
class Th extends React.Component {
handleClick = () => {
const { onClick, id } = this.props;
onClick(id);
};
render() {
const { value } = this.props;
return <th onClick={this.handleClick}>{value}</th>;
}
}
class App extends React.Component {
state = {
users: []
};
async componentDidMount() {
const res = await fetch(
`https://run.mocky.io/v3/6982a190-6166-402e-905f-139aef40e6ef`
);
const users = await res.json();
this.setState({
users
});
}
handleSort = id => {
this.setState(prev => {
return {
[id]: !prev[id],
users: prev.users.sort((a, b) =>
prev[id] ? a[id] < b[id] : a[id] > b[id]
)
};
});
};
render() {
const { users } = this.state;
return (
<table>
<thead>
<tr>
<Th onClick={this.handleSort} id="mileage" value="Mileage" />
<Th onClick={this.handleSort} id="overall_score" value="Overall score" />
<Th onClick={this.handleSort} id="fuel_consumed" value="Fuel Consumed" />
</tr>
</thead>
<tbody>
{users.map(user => (
<tr>
<td>{user.span.mileage.value}</td>
<td>{user.span.overall_score.value}</td>
<td>{user.span.fuel_consumed.value}</td>
</tr>
))}
</tbody>
</table>
);
}
}
export default App;
To make it works you need to change a few thigs:
the setState merges new data with old one, so [id]: !prev[id] adds new property to state for each column you filter without removing old one. It's better to store column to filter in dedicated state property (e.g. sortBy).
fix sorting function to make it sorting the users by correct object properties
remove async from componentDidMount and change fetch to use then/catch instead of async/await (it makes your code more React-ish).
Use example below as an inspiration:
class App extends React.Component {
state = {
sortBy: null,
order: "ASC",
users: []
};
componentDidMount() {
fetch(`https://run.mocky.io/v3/6982a190-6166-402e-905f-139aef40e6ef`)
.then(response => response.json())
.then(users => this.setState({users}))
.catch(err => console.log('Error', err));
}
handleSort = id => {
this.setState(prev => {
const ordered = prev.users.sort((a, b) =>
prev.order === "ASC"
? a["span"][id]["value"] < b["span"][id]["value"]
: a["span"][id]["value"] > b["span"][id]["value"]
);
return {
sortBy: id,
order: prev.order === "ASC" ? "DESC" : "ASC",
users: ordered
};
});
};
render() {
const { users } = this.state;
return (
<table>
<thead>
<tr>
<Th onClick={this.handleSort} id="mileage" value="Mileage" />
<Th
onClick={this.handleSort}
id="overall_score"
value="Overall score"
/>
<Th
onClick={this.handleSort}
id="fuel_consumed"
value="Fuel Consumed"
/>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr>
<td>{user.span.mileage.value}</td>
<td>{user.span.overall_score.value}</td>
<td>{user.span.fuel_consumed.value}</td>
</tr>
))}
</tbody>
</table>
);
}
}
class Th extends React.Component {
handleClick = () => {
const { onClick, id } = this.props;
onClick(id);
};
render() {
const { value } = this.props;
return <th onClick={this.handleClick}>{value}</th>;
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>
Keep in mind that in only works with the current data schema and fields you already have. If you want to change the fields to sort by you need to update sorting function.

Dynamically parse data for table rows

I am trying to parse the data dynamically for the table. So far I have tried the following to display the table.
renderTableData = () => {
return this.props.data.map((item, index) => {
const { name, value } = item;
return (
<tr key={index}>
<td>{name}</td>
<td>{value}</td>
</tr>
);
});
};
Here I am hardcoding the field values for displaying. I need this to be dynamic
Full code: https://codesandbox.io/s/react-basic-class-component-3kpp5?file=/src/Table.js:0-805
import * as React from "react";
class Table extends React.Component {
renderTableData = () => {
return this.props.data.map((item, index) => {
const { name, value } = item;
return (
<tr key={index}>
<td>{name}</td>
<td>{value}</td>
</tr>
);
});
};
renderTableHeader = () => {
let header = Object.keys(this.props.data[0]);
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>;
});
};
render() {
return (
<div>
<table>
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
</tbody>
</table>
</div>
);
}
}
export default Table;
You can loop through object properties with Object.entries
renderTableData = () => {
return this.props.data.map((item, index) => {
return (
<tr key={index}>
{Object.entries(item).map(([key, value])=> <td key={key}>{value}</td>)}
</tr>
);
});
};
However, as you can see you lost control of the order of columns. Additionaly there might be columns you don't wish to display.
You can tackle that by appending Object.entries with custom implemented functions
<tr key={index}>
{Object.entries(item)
.filter(predicateFunction)
.sort(sortingFunction).map(([key, value])=> <td key={key}>{value}</td>)}
</tr>
Or switch to react-data-table

Filter method on another method?

I want to run a filter method on the renderTable method that I've defined below. Is it possible to do:
renderTable().filter(x => x.name)?
Right now, I have a table with rows of each category and their results from a url that provided some json data.
I would like to make a feature that allows users to adjust the setting to return their desired results.
CODE
const renderTable = () => {
return players.map(player => {
return (
<tr>
<td>{player.name}</td>
<td>{player.age}</td>
<td>{player.gender}</td>
<td>{player.state}</td>
<td>{player.status}</td>
</tr>
)
})
}
return (
<div className = "App">
<h1>Players</h1>
<table id = "players">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
<th>State</th>
<th>Status</th>
</tr>
</thead>
<tbody>{renderTable()}</tbody>
</table>
</div>
);
You can apply the filter before the map. Here, give this a try:
const renderTable = () => {
return players
.filter(player => player.state === "NSW")
.map(player => {
return (
<tr>
<td>{player.name}</td>
<td>{player.age}</td>
<td>{player.gender}</td>
<td>{player.state}</td>
<td>{player.status}</td>
</tr>
);
});
};
Here's a Working Sample CodeSandbox for your ref.
Yes you can. Just filter before map players.filters(...).map. But this is better
const renderTable = () => {
const rows = []
players.forEach((player, index) => {
if(player.name) {
rows.push(
<tr key={index}>
<td>{player.name}</td>
<td>{player.age}</td>
<td>{player.gender}</td>
<td>{player.state}</td>
<td>{player.status}</td>
</tr>
)
}
})
return rows;
}

Component not rendering

I have a component that won't render it's sub-component. There's no errors in the console. I get the data I need from the web call, no errors with that. Not sure why the Project component isn't rendering anything.
Data Retrieval functions in separate file:
window.getCurrentUsersGroups = function() {
var d = $.Deferred();
var currentUsersBusinessArea = null;
var userGroups = $().SPServices({
operation: "GetGroupCollectionFromUser",
userLoginName: $().SPServices.SPGetCurrentUser()
});
userGroups.then(function(response) {
var groups = [];
$(response).find("Group").each(function() {
var self = $(this);
groups.push(self.attr("Name"))
});
currentUsersBusinessArea = _.filter(groups, function(group) {
return _.startsWith(group, "BusinessArea")
});
d.resolve(getListings(currentUsersBusinessArea[0]))
})
return d.promise();
}
window.getListings = function(businessArea) {
var d = $.Deferred();
var projects = [];
var listings = $().SPServices.SPGetListItemsJson({
listName: "Projects",
CAMLQuery: "<Query><Where><Eq><FieldRef Name='" + businessArea + "'/><Value Type='String'>Unassigned</Value></Eq></Where></Query>"
});
listings.then(function() {
var result = this.data;
result.map(function(project){
projects.push({
id: project.ID,
pID: project.ProjectID,
title: project.Title,
status: project.Status,
created: project.Created,
businessArea: project.BusinessAreaFinanceAccounting,
sponsor: project.SponsoringArea,
comments: project.Comments
})
})
d.resolve({businessArea: businessArea, projects: projects})
})
return d.promise();
}
Listing Component:
class Listings extends React.Component {
constructor(props) {
super(props);
this.state = {
businessArea: null,
projects: [],
};
};
componentDidMount() {
let that = this;
window.getCurrentUsersGroups().then(function(response) {
response.then(function(data){
that.setState({businessArea: data.businessArea})
that.setState({projects: data.projects})
})
})
};
render() {
let {businessArea, projects} = this.state;
console.log(this.state)
return (
<section className="listingsContainer">
<h3>{businessArea}</h3>
<hr></hr>
<table className="ms-Table">
<thead>
<tr>
<th>Edit</th>
<th>Project ID</th>
<th>Project Name</th>
<th>Response Status</th>
<th>Initiated Date</th>
<th>BA Impact</th>
<th>Sponsor</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
{
projects.map( function({project,index}) {
console.log(project.ID)
return <Project key={project.id} project={project} index={index} />
})
}
</tbody>
</table>
</section>
)
}
}
Project Component:
const Project = ({project, index}) => {
return (
<tr key={index + project.ID}>
<td>
<a href={_spPageContextInfo.webAbsoluteUrl + '/SitePages/Business%20Project%20Edit.aspx?ProjectID=' + project.ID}>
<span style="font-size:1em;" className="ms-Icon ms-Icon--editBox"></span>
</a>
</td>
<td>{project.ProjectID}</td>
<td>{project.Title}</td>
<td>{project.Status}</td>
<td>{project.Created}</td>
<td>{project.BusinessAreaFinanceAccounting}</td>
<td>{project.SponsoringArea}</td>
<td>{project.Comments}</td>
</tr>
);
};
Browser Result:
If I output $r in the console, the Listing component state has projects in it. But the react dev tool says the array is 0 and nothing is rendering. Confused.
Seems like you forgot to wrap your project with curly braces:
const Project = ({project}) => {
return (
<tr>
<td>
<a href={_spPageContextInfo.webAbsoluteUrl + '/SitePages/Business%20Project%20Edit.aspx?ProjectID=' + project}>
<span style="font-size:1em;" className="ms-Icon ms-Icon--editBox"></span>
</a>
</td>
<td>{project.ProjectID}</td>
<td>{project.Title}</td>
<td>{project.Status}</td>
<td>{project.Created}</td>
<td>{project.BusinessAreaFinanceAccounting}</td>
<td>{project.SponsoringArea}</td>
<td>{project.Comments}</td>
</tr>
);
};
Here's a nicer way to handle the Component's:
class Listings extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
businessArea: null
};
}
componentDidMount() {
let that = this;
let groups = [];
let userGroups = $().SPServices({
operation: "GetGroupCollectionFromUser",
userLoginName: $().SPServices.SPGetCurrentUser()
});
userGroups.then((response) => {
$(response).find("Group").each(function() {
let self = $(this);
groups.push(self.attr("Name"))
});
let currentUsersBusinessArea = _.filter(groups, (group) => _.startsWith(group, "BusinessArea"));
this.setState({businessArea: currentUsersBusinessArea})
}).then(getListings)
function getListings() {
let listings = $().SPServices.SPGetListItemsJson({
listName: "Projects",
CAMLQuery: "<Query><Where><Eq><FieldRef Name='" + that.state.businessArea + "'/><Value Type='String'>Unassigned</Value></Eq></Where></Query>"
});
listings.then(function() {
that.setState({data: this.data});
})
};
}
render() {
let {data, businessArea} = this.state;
return (
<section className="listingsContainer">
<h3>{`Business Area ${businessArea}`}</h3>
<hr></hr>
<table className="ms-Table">
<thead>
<tr>
<th>Edit</th>
<th>Project ID</th>
<th>Project Name</th>
<th>Response Status</th>
<th>Initiated Date</th>
<th>BA Impact</th>
<th>Sponsor</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
{data.map({project,index}) => <Project key={index} project={project} /> }
</tbody>
</table>
</section>
)
}
}
const Project = ({project}) => (
<tr>
<td>
<a href={_spPageContextInfo.webAbsoluteUrl`/SitePages/Business%20Project%20Edit.aspx?ProjectID=${project}`}>
<span style={{'fontSize':'1em'}} className="ms-Icon ms-Icon--editBox"></span>
</a>
</td>
<td>{project.ProjectID}</td>
<td>{project.Title}</td>
<td>{project.Status}</td>
<td>{project.Created}</td>
<td>{project.BusinessAreaFinanceAccounting}</td>
<td>{project.SponsoringArea}</td>
<td>{project.Comments}</td>
</tr>
);
Tip: Never Mix Jquery with React Components
The main reason it wasn't rendering is because of this table cell:
<td>
<a href={_spPageContextInfo.webAbsoluteUrl + '/SitePages/Business%20Project%20Edit.aspx?ProjectID=' + project.ID}>
<span style="font-size:1em;" className="ms-Icon ms-Icon--editBox"></span>
</a>
</td>
The span tag as a style property. Which is preventing it from rendering from what I can see.

Categories

Resources