I am a noob at React and have gotten myself a little lost. I am creating my react in a .jsx file which I run grunt-babel on, this then gets put through grunt-uglify to concatenate it with react.js and react-dom.js (in a set order) and finally the single outputted file is then added to my html.
I am using react to build a table. I pass in an array of data for the table, but then I want to splice the array and paginate it.
I've found a 3rd party component that looks like it will do the job (https://github.com/SimonErich/react-paginatr). Now my problem is that I don't know how to actually use this with my workflow. I have tried various other 3rd party components and don't know how to get them to work either.
If I simply add the compiled PaginatrComponent.js PaginatrMixin.js in the libs folder to my uglify command after react and react-dom I get this in the console
Uncaught ReferenceError: module is not defined at react.min.js:8320
And I doing something completely wrong? I see people making references to CommonJs and Webpack and Browserify? But am not sure what they do or how they would fit in my workflow.
My code is on codepen here http://codepen.io/rmaspero/pen/LxQNYY:
var INVOICES = [
{
state: "processing",
number: "INV-31",
customer: "Dael ltd",
total: 60000,
currency: "£",
due: "5 Days",
uri: "https://www.example.com/",
id: 1,
},
{
state: "rejected",
number: "INV-765",
customer: "Dael ltd",
total: 7430,
currency: "€",
due: "30 Days",
uri: "https://www.example.com/2",
id: 2,
},
{
state: "rejected",
number: "INV-001",
customer: "JB Towers ltd",
total: 943,
currency: "£",
due: "15 Days",
uri: "https://www.example.com/3",
id: 3,
},
{
state: "draft",
number: "INV-043",
customer: "JB Towers ltd",
total: 72,
currency: "£",
due: "10 Days",
uri: "https://www.example.com/4",
id: 4,
},
{
state: "processing",
number: "INV-341",
customer: "Dael ltd",
total: 3045,
currency: "£",
due: "45 Days",
uri: "https://www.example.com/5",
id: 5,
},
{
state: "processing",
number: "INV-501",
customer: "JB Towers ltd",
total: 453,
currency: "£",
due: "65 Days",
uri: "https://www.example.com/6",
id: 6,
},
];
function Invoice(props) {
return (
<tr className='invoice-table--row' onClick={props.onLink}>
<td className='invoice-table--cell invoice__state'><span className={"state__indicator indicator--" + props.state}></span><span className="state__text">{props.state}</span></td>
<td className='invoice__number'>{props.number}</td>
<td className='invoice__customer small-mobile-hide'>{props.customer}</td>
<td className='invoice-table--cell'>{props.currency}{props.total}</td>
<td className='invoice__due'>{props.due}</td>
</tr>
);
}
Invoice.propTypes = {
onLink: React.PropTypes.func.isRequired,
}
function TableHeadings() {
return (
<thead>
<tr className='invoice-table--header'>
<th className='invoice-table--head invoice-head__state'>State</th>
<th className='invoice-table--head'>Inv No.</th>
<th className='invoice-table--head small-mobile-hide'>Customer</th>
<th className='invoice-table--head'>Total</th>
<th className='invoice-table--head invoice-head__due'>Due</th>
</tr>
</thead>
);
}
function TableTitle(props) {
return (
<div className="section-divider">
<h3 className="section-divider__title">Processing React</h3>
<div className="paginate">
<a className='paginate__button' href="#"><span className="icon icon--arrow icon--large arrow--previous" data-grunticon-embed></span></a>
<span className="paginate__text">Page 1 of 3</span>
<a className='paginate__button' onClick={props.onPage}><span className="icon icon--arrow icon--large" data-grunticon-embed></span></a>
</div>
</div>
);
}
TableTitle.propTypes = {
onPage: React.PropTypes.func.isRequired,
}
var Dashboard = React.createClass({
propTypes: {
rows: React.PropTypes.number.isRequired,
startrow: React.PropTypes.number,
initialInvoices: React.PropTypes.arrayOf(React.PropTypes.shape({
state: React.PropTypes.string.isRequired,
number: React.PropTypes.string.isRequired,
customer: React.PropTypes.string.isRequired,
total: React.PropTypes.number.isRequired,
currency: React.PropTypes.string.isRequired,
due: React.PropTypes.string.isRequired,
id: React.PropTypes.number.isRequired,
})).isRequired,
},
getDefaultProps: function() {
return {
startrow: 0,
}
},
getInitialState: function() {
return {
invoices: this.props.initialInvoices,
rows: this.props.rows,
};
},
onPageUp: function() {
this.state.invoices.slice(this.props.startrow + this.props.row, this.props.rows);
},
onLinkClick: function(uri) {
console.log(uri);
window.location.href = (uri);
},
render: function() {
return (
<div>
<TableTitle onPage={function() {this.onPageUp()}.bind(this)}/>
<table className='invoice-table'>
<TableHeadings/>
<tbody>
{this.state.invoices.slice(this.props.startrow, this.props.rows).map(function(invoice, index) {
return (
<Invoice
state={invoice.state}
number={invoice.number}
customer={invoice.customer}
total={invoice.total}
currency={invoice.currency}
due={invoice.due}
key={invoice.id}
uri={invoice.uri}
onLink={function() {this.onLinkClick(invoice.uri)}.bind(this)}
/>
);
}.bind(this))}
</tbody>
</table>
</div>
);
}
});
ReactDOM.render(<Dashboard initialInvoices={INVOICES} rows={3} totalRows={6} />, document.getElementById('dashboard-processing__table'));
I think that you should definitely use a tool like Webpack or Browserfy. They allow you to pack your project making references of files way easier. This is good explanation about Webpack. I would also add babel and use React with ES6. The official React docs are using ES6 and I find its syntax way better. All these tools will help you to keep each component in a separate file and they will allow you to reference and use them (or 3rd party components) straightforwardly.
You should probably check out a tutorial/boilerplate. This one looks pretty good to me but there are plenty of resources out there.
Related
I am new to react js. I am trying to use the map function on an array that is stored in another file called products.js , when I console log the data it is showing by not showing in the screen. This is my code for the component:
import React from 'react'
import products from '../products'
import {Row,Col} from 'react-bootstrap'
const HomeScreen = () => {
console.log(products)
return (
<>
<h1>Latest Products</h1>
<Row>
{products.map(product => {
<h3 key={product._id}>{product.name}</h3>
})}
</Row>
</>
)
}
export default HomeScreen
This is the product.js code:
const products = [
{
_id: '1',
name: 'Airpods Wireless Bluetooth Headphones',
image: '/images/airpods.jpg',
description:
'Bluetooth technology lets you connect it with compatible devices wirelessly High-quality AAC audio offers immersive listening experience Built-in microphone allows you to take calls while working',
brand: 'Apple',
category: 'Electronics',
price: 89.99,
countInStock: 10,
rating: 4.5,
numReviews: 12,
},
{
_id: '2',
name: 'iPhone 11 Pro 256GB Memory',
image: '/images/phone.jpg',
description:
'Introducing the iPhone 11 Pro. A transformative triple-camera system that adds tons of capability without complexity. An unprecedented leap in battery life',
brand: 'Apple',
category: 'Electronics',
price: 599.99,
countInStock: 7,
rating: 4.0,
numReviews: 8,
},
{
_id: '3',
name: 'Cannon EOS 80D DSLR Camera',
image: '/images/camera.jpg',
description:
'Characterized by versatile imaging specs, the Canon EOS 80D further clarifies itself using a pair of robust focusing systems and an intuitive design',
brand: 'Cannon',
category: 'Electronics',
price: 929.99,
countInStock: 5,
rating: 3,
numReviews: 12,
},
{
_id: '4',
name: 'Sony Playstation 4 Pro White Version',
image: '/images/playstation.jpg',
description:
'The ultimate home entertainment center starts with PlayStation. Whether you are into gaming, HD movies, television, music',
brand: 'Sony',
category: 'Electronics',
price: 399.99,
countInStock: 11,
rating: 5,
numReviews: 12,
},
{
_id: '5',
name: 'Logitech G-Series Gaming Mouse',
image: '/images/mouse.jpg',
description:
'Get a better handle on your games with this Logitech LIGHTSYNC gaming mouse. The six programmable buttons allow customization for a smooth playing experience',
brand: 'Logitech',
category: 'Electronics',
price: 49.99,
countInStock: 7,
rating: 3.5,
numReviews: 10,
},
{
_id: '6',
name: 'Amazon Echo Dot 3rd Generation',
image: '/images/alexa.jpg',
description:
'Meet Echo Dot - Our most popular smart speaker with a fabric design. It is our most compact smart speaker that fits perfectly into small space',
brand: 'Amazon',
category: 'Electronics',
price: 29.99,
countInStock: 0,
rating: 4,
numReviews: 12,
},
]
export default products
And this is my App.js code :
import React from 'react'
import {Container} from 'react-bootstrap'
import Header from './components/Header'
import Footer from './components/Footer'
import HomeScreen from './screens/HomeScreen'
const App=()=> {
return (
<>
<Header />
<main>
<Container className='py-3'>
<HomeScreen/>
</Container>
</main>
<Footer/>
</>
);
}
export default App;
Why is this happening? I have tried looking up some solutions but failed. Thank you for helping
Add a return statement in the map function.
{products.map((product) => {
return <h3 key={product._id}>{product.name}</h3>;
})}
I think the problem is that you need to add a return statement before the h3:
<Row>
{products.map(product => {
return <h3 key={product._id}>{product.name}</h3>
})}
</Row>
If you want to do it this way without using 'return', there are 2 options:
Option 1: Remove the curly braces
<Row>
{products.map(product => return <h3 key={product._id}>{product.name}</h3>)}
</Row>
Option 2: Use () instead of the curly braces (Most especially when you are gonna add more elements later)
<Row>
{products.map(product => (
<h3 key={product._id}>{product.name}</h3>
<...other html elements>
))}
</Row>
I have an array of objectsand I want to show only 3 at a time with a button that when clicked,show 3 more, making 6 in total and so on.
This is what I have currently:
// vue template
<ul>
<li v-for="(order, index) in orders" :key="index">
{{order.item_description}}
</li>
</ul>
<button #click="loadMore">load more </button>
For the script, I have the data and some computed properties:
data() {
return {
orders: [
{
id: 1,
item_description: "One",
created_at: "23, Dec 2019",
delivery_address: "Location",
cost: "2500"
},
{
id: 2,
item_description: "Two",
created_at: "23, Dec 2019",
delivery_address: "Location",
cost: "2500"
},
{
id: 3,
item_description: "Three",
created_at: "23, Dec 2019",
delivery_address: "Location",
cost: "2500"
}
// .... upto 12 items
],
currentPage: 1,
maxPerPage: 3
}
},
computed: {
totalResults() {
return Object.keys(this.orders).length
},
pageCount() {
return Math.ceil(this.totalResults / this.maxPerPage)
},
pageOffest() {
return this.maxPerPage * this.currentPage
}
},
methods: {
loadMore() {
this.currentPage += 1
}
}
}
My confusion is how to get the orders to be paginated using the maxPerPage and then add more using the loadMore
Here is a codesandbox for demo
You can add computed property like:
paginatedOrders() {
return this.orders.slice(0, this.currentPage * this.maxPerPage);
}
And then make your loop with this property:
<li v-for="(order, index) in paginatedOrders" :key="index">{{order.item_description}}</li>
additonnaly you can hide read more button if you already display all items
<button #click="loadMore" v-if="currentPage * maxPerPage < orders.length">load more</button>
Check it on codesandbox
Note: Keep in mind that the most common use of the "load more" button is to load other items with an api call or ajax request. This only works if you load all your orders beforehand.
I have a React/TypeScript component I'm building that features an HTML table for contact details.
I'm trying to map the API response to cells in the table and dynamically populate rows for each contact and their details. There are two arrays for phone numbers and addresses that are nested deep within the object and I can't figure out how to iterate over them along with the rest of the data all in one go.
I initially tried nested for loops but I hit a wall when I got to those two elements because of their position in the data object.
I then tried to use .map() in the middle of the for loops, but I hit a TypeScript error stating the element I'm trying to map over could possibly be null.
I thought about iterating over phone number and address arrays separately and then inserting them into the appropriate cells per contact but I can't figure out how to do when I'm using separate for loops to populate the other cells.
Expected Output:
Name | Member | Telephone | Email | Addresses
Ben B| Friend | 610-535-1234 | ben#gmail.com | 123 Fiction Drive,Denver
215-674-6789 234 Dreary Ln,Seattle
Alice | Family| 267-333-1234 | ally#aim.com | 437 Chance St, Pitts.
I made a CodeSandbox and dropped the current component and example data structure below. For the CodeSandbox it currently loads but as soon as you uncomment these lines you'll see the error
<td>{contacts.contactGroups[i].contacts[j].phoneNumbers}</td>
<td>{contacts.contactGroups[i].contacts[j].addresses}</td>
Current Component
import React from "react";
import { Contacts } from "./contact-types";
type Props = {
contacts: Contacts;
};
export const ContactsGrid = (props: Props) => {
const { contacts } = props;
const rows = [];
for (let i = 0; i < contacts.contactGroups.length; i++) {
rows.push(
<tr>
<td>{contacts.contactGroups[i].contactGroup}</td>
</tr>
);
for (let j = 0; j < contacts.contactGroups[i].contacts.length; j++) {
rows.push(
<tr>
<td>{contacts.contactGroups[i].contacts[j].fullName}</td>
<td>{contacts.contactGroups[i].contacts[j].member}</td>
{/* <td>{contacts.contactGroups[i].contacts[j].phoneNumbers}</td> */}
<td>{contacts.contactGroups[i].contacts[j].email}</td>
{/* <td>{contacts.contactGroups[i].contacts[j].addresses}</td> */}
</tr>
);
}
}
return (
<table>
<thead>
<tr>
<td>Name</td>
<td>Member Type</td>
<td>Telephone</td>
<td>Email</td>
<td>Address</td>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
};
Current Data Structure
export default {
count: 1,
contactGroups: [
{
contactGroup: "Family",
count: 1,
contacts: [
{
member: "Uncle",
fullName: "BENJAMIN BILLIARDS",
lastName: "BILLIARDS",
firstName: "BENJAMIN",
email: "shark#billiards.com",
phoneNumbers: [
{
telephoneNumber: "123-456-7899",
type: "mobile"
},
{
telephoneNumber: "610-555-7625",
type: "work"
}
],
addresses: [
{
addressLine1: "123 FAMILY ST",
addressLine2: "APT 1208",
city: "ATLANTA",
state: "GEORGIA",
zipCode: "12345"
},
{
addressLine1: "456 WORKING BLVD",
addressLine2: "",
city: "ATLANTA",
state: "GEORGIA",
zipCode: "12345"
}
]
}
]
},
{
contactGroup: "Friends",
count: 1,
contacts: [
{
member: "School Friend",
fullName: "HANS ZIMMER",
lastName: "ZIMMER",
firstName: "HANS",
email: "hans#pirates.com",
phoneNumbers: [
{
telephoneNumber: "267-455-1234",
type: "mobile"
}
],
addresses: [
{
addressLine1: "789 FRIEND ST",
addressLine2: "",
city: "SAN DIEGO",
state: "CALIFORNIA",
zipCode: "67890"
},
{
addressLine1: "234 CANARY ST",
addressLine2: "",
city: "SEATTLE",
state: "WASHINGTON",
zipCode: "67890"
}
]
}
]
}
]
};
Use a nested map:
const rows = contacts.contactGroups.map(group => <tr>
<td>{group.contactGroup}</td>
<td>
<table>
{group.contacts.map(contact => <tr>
<td>{contact.fullName}
</tr>}
</table>
</td>
</tr>;
I have created a demo website to sell products to customers. This website uses filters/search/sort etc to ease navigating through different products. The issue I have is related to filtering and search. I want to make my filters such that they work on the result of search. I have attempted this using checkboxes and the computed properties in Vue.
HTML
<div id="app">
<h5>Search</h5>
<input type="text" v-model="search" placeholder="Search title.."/><br><br>
<h5>Filter</h5>
<li v-for="filter in possibleFilters" :key="filter">
<label>
<input type="checkbox" #change="toggleFilter(filter)" :checked="filters.includes(filter)">
<span >{{filter}}</span>
</label>
</li>
<div class="block" v-for="(product, index) in filteredSearch">
<hr>
<h3>{{product.name}}</h3>
<p>Price: £{{product.price}}</p>
<p>Location: {{product.location}}</p>
<hr>
</div>
</div>
JavaScript
new Vue({
el: "#app",
data: {
filters: [],
search: "",
products: [{
name: "milk",
location: "London",
price: 100
},
{
name: "oranges",
location: "Birmingham",
price: 80
},
{
name: "apples",
location: "Edinburgh",
price: 90
},
{
name: "bananas",
location: "Paris",
price: 120
},
{
name: "bread",
location: "Paris",
price: 110
},
{
name: "water",
location: "Oslo",
price: 90
},
{
name: "soda",
location: "London",
price: 90
},
{
name: "tea",
location: "Oslo",
price: 120
},
{
name: "bakedbeans",
location: "Oslo",
price: 140
}
],
},
methods: {
toggleFilter(newFilter) {
this.filters = !this.filters.includes(newFilter) ?
[...this.filters, newFilter] :
this.filters.filter(f => f !== newFilter)
}
},
computed: {
possibleFilters() {
return [...new Set(this.filteredSearch.map(x => x.location))]
},
filteredSearch() {
return this.products.filter(p => {
var searchProduct = p.name.toLowerCase().includes(this.search.toLowerCase());
var filterProduct = this.filters.length == 0 || this.filters.includes(p.location);
return searchProduct && filterProduct
})
}
},
})
The problem is I cannot select the filter more than once. The filter is based on the location, my goal is to be able to apply the filter more than once. At the moment I can only select one filter at a time.
i.e if I search for "l" it returns milk and apples, the filters shows London and Edinburgh, I can only select either London or Edinburgh but not both. If I select London, it should only show me "Milk" while still showing me the option of 'Edinburgh' and when I select both it should show me both "Milk" and "Apples"
A fiddle showing the problem:
https://jsfiddle.net/Calv7/L1vnqh63/9/
Any help will be appreciated. Thanks.
Does this solve your problem?
possibleFilters() {
return [...new Set(this.products.map(x => x.location))]
},
Here a quick and dirty solution based on your fiddle. Just to give you an idea how to separate both filters.
Try the solution here https://jsfiddle.net/4839spkx/
possibleFilters() {
// we have some input, so show all location filters available in the filtered by input products
if (this.search) {
const filteredByInput = this.products.filter(p => p.name.includes(this.search.toLowerCase()))
return [...new Set(filteredByInput.map(p => p.location))]
}
return [...new Set(this.filteredSearch.map(x => x.location))]
},
Since your using a computed property to dynamically generate your possible filters from the filtered products locations this will update everytime you update your filtered products. I would recommend you to create a new Array and to populate this Array with your data.
HTML:
...
<li v-for="filter in possibleFiltersArr" :key="filter">
<label>
<input type="checkbox" #change="toggleFilter(filter)" :checked="filters.includes(filter)">
<span>{{filter}}</span>
</label>
</li>
...
JS
...
data: {
posibleFiltersArr:[],
...
created(){
this.possibleFiltersArr=[...new Set(this.filteredSearch.map(x => x.location))]
},
...
You can set this array in the created() method and you could update this array after inputing some text on the searchbox.
const users =
[
{
name: "Joy soap",
customerName: "Salomy",
date: "19 March 2018",
time: "08:46am",
amount: 3000,
status: "paid",
number: 24,
images:
"https://snack1.amazonaws.com/~asset/9d799c33cbf767ffc1a72e53997218f7"
},
{
name: "Closeup tooth paste",
customerName: "Salomy",
date: "19 March 2018",
time: "08:46am",
amount: 3000,
status: "paid",
number: 20,
images:
"https://1.amazon4d99c3d76575cc03c2a7f816280"
},
{
name: "Iman Powder",
customerName: "Emanbe",
date: "20 March 2018",
time: "11:25am",
amount: 3000,
status: "paid",
number: 12,
images:
"https://1.amazonaws.com/~asset/ee06c63d01543a44631c3421df6ee5fa"
},
{
name: "John Bellion",
customerName: "Okonkwo Chioma",
date: "20 March 2018",
time: "08:46am",
amount: 3000,
status: "paid",
number: 3,
images:
"https://sn1.amazonaws.com/~asset/ee06c63d01543a44631c3421df6ee5fa"
}
];
Please I have an array of objects like the above that I want to render in a ListView with a Section Header function pointing to user.date... I want it to render a list of all the items on 19 March 2018, and then render the items on 20 March 2018 under the header also.
I have used ListView several times but I have never been able to use the section header in this way with the above arrays of object. Please a detailed explanation would be greatly appreciated. I know its probably a simple task to some of you but please be kind. I need a renderSectionHeader() function that can organize the data with respect to their dates so I can render it in my listview like this
`<ListView
ref={ref => (this.scrollView = ref)}
onContentSizeChange={() => {
this.scrollView.scrollToEnd({ animated: false});
}}
dataSource={this.state.userDataSource}
renderRow={this.renderRow.bind(this)}
renderSectionHeader={this.renderSectionHeader.bind(this)}
/>`
An example of what I want is here but I want to know how it can be done with the above array
ListView is no longer available in React-Native. You can use FlatList. Its very simple then ListView as well.
1) Import FlatList from react-native:
import { FlatList } from 'react-native';
2) Copy Below code into your render() method.
<FlatList
horizontal={true/false}
style={YOUR_COSTOM_STYLE}
data={YOUR_ARRAY}
renderItem={this.renderItem.bind(this)}
keyExtractor={(item, index) => String(index)}
/>
3) You do necessary stuff for your cell in renderItem method.
// Render Methods
renderItem({ item }) {
return (
// DO_YOUR_STUFF
);
}
You can more detail about FlatList from here.
One more example for header in FlatList