I created a custom item for my Flat List which shows the name and the image ( retrieved by his url) of a certain product. The user can filter this products by their names, and the new filtered data is passed again to the Flat List. The problem is that whenever I filter the data, Flat List re-renders every custom item, so the Image components inside have to download the pic again, causing a flickering effect. This is an approximate example of what I mean:
function test(){
initialData=[
{
text: 'product A',
image: 'https://picsum.photos/200/300',
id: 1
},
{
text: 'product B',
image: 'https://picsum.photos/200/300',
id: 2
},
{
text: 'product C',
image: 'https://picsum.photos/200/300',
id: 3
},
]
const [filteredData, setFilteredData] = useState(initialData);
const handlePress = () =>(
setFilteredData(filterData(data)); // Filter data by some parameters
)
const renderItem = (props) => {
return(
<View>
<Image>{props.text}</Image>
<Text source={{uri: props.image}}></Text>
</View>
)
}
return(
<>
<Button onPress={() => handlePress()}></Button>
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>
</>
)
}
first must to say in order to avoiding unnecessary rendering data you are be able to use useMemo and useCallback methods (in functional programming) for memorizing your variables and your functions (check hooks React hooks documents). some procedures can help you for improving your code:
Don't put the static data inside your function, for example put initialData outside of your test function or memorize it by useMemo.
Don't use inline function inside components and as far as possible memorize them.
You can create independent component for your FlatList item and memorize it by memo then use them or memorize renderItem inside your test function.
For use image in your app (specifically when you have large numbers of image) use react-native-fast-image that help you avoiding to re-rendering images by caching them.
Related
I need to load all the products in my nodeJS application with WooCommerce Rest Api. I use the WooCommerce REST API - JavaScript Library and the Syncfusion Grid Component. Because I can't load all data at once, I wanted to use the Load data on demand like this, but I can't find any documentation or examples on this.
I have something like this:
import React from 'react';
import { useEffect, useState } from "react";
import { GridComponent, ColumnsDirective, ColumnDirective, Resize, Sort, ContextMenu, Filter, Page, ExcelExport, PdfExport, Edit, Inject } from '#syncfusion/ej2-react-grids';
import WooCommerceRestApi from "#woocommerce/woocommerce-rest-api";
var WooCommerce = new WooCommerceAPI({
url: 'http://example.com',
consumerKey: 'ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
consumerSecret: 'cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
wpAPI: true,
version: 'wc/v1'
});
const WooCommerceProducts = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchOrders();
}, []);
let fetchOrders = () => {
WooCommerce
.get("products", {
per_page: 100,
page: 1
})
.then((response) => {
if (response.status === 200) {
setProducts(response.data);
}
})
.catch((error) => { });
};
return (
<div className='m-2 md:m-10 p-2 md:p-10 bg-white rounded-3xl'>
<Header category="Page" title="WooCommerce Orders" />
<GridComponent
id='gridcomp'
dataSource={orders}
allowPaging
allowSorting>
<ColumnsDirective>
<ColumnDirective field='id' />
<ColumnDirective field='name' />
<ColumnDirective field='slug' />
<ColumnDirective field='status' />
...
</ColumnsDirective>
<Inject services={[Resize, Sort, ContextMenu, Filter, Page, ExcelExport, PdfExport]} />
</GridComponent>
</div>
)
}
export default WooCommerceProducts
Please help and thx
If you are using any custom services, I suggest you use the custom-binding feature to bind the data to the grid. I would like to share the behavior of custom-binding in EJ2 Grid.
For every grid action (such as Filter, Page, etc.,), I have triggered the dataStateChange event, and, in the event arguments, I have sent the corresponding action details (like skip, take, filter field, value, sort direction, etc.,) Based on that, you can perform the action in your service, return the data as a result, and count objects.
Note: ‘dataStateChange’ event is not triggered at the Grid initial render. If you are using a remote service, you need to call your remote service by manually with a pagination query (need to set the skip value as 0 and take a value based on your pageSize of pageSettings in Grid. If you are not defined pageSize in pageSettings, you need to send the default value 12 ) in load event of Grid. Please return the result like as "{result: […], count: …}" format to Grid.
‘dataSourceChanged’ event is triggered when performing CRUD actions in Grid. You can perform the CRUD action in your service using action details from this event, and, you need to call the endEdit method to indicate the completion of the save operation.
Custom-binding: https://ej2.syncfusion.com/react/documentation/grid/data-binding/data-binding/#custom-binding
Demo: https://ej2.syncfusion.com/react/demos/#/material/grid/custom-binding
sample: https://stackblitz.com/edit/react-v64sms-wx3hsy?file=index.js
I want to give my dropdown items to use the text themeColor. How can I achieve this? Below is my code:
const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: "50%" },
dropdownItem: {
backgroundColor:"$themePrimary",
color:"$ms-color-themePrimary"
}
};
<Dropdown label='Select your List' styles={ dropdownStyles} placeholder='---Select an Option---' options={this.state.listNames}></Dropdown>
The above does not work. The dropdown items dont use the Primary color which is #a200ff.
As far as I know the "$ms-Bla--bla-bla" notation is only working for (statically) pre-processed fabric-ui files, there is no run-time processing of these variables in spfx. So, you may need to use the theme directly. For example, you could make your dropdownStyles a function instead of a constant. You receive the current theme in parameters then:
const dropdownStyles = (styleProps: IDropdownStyleProps): Partial<IDropdownStyles> => {
return {
dropdown: { width: "50%" },
dropdownItem: {
backgroundColor: styleProps.theme.palette.themePrimary,
color: styleProps.theme.palette.themePrimary
}
}
};
There are other options as well, like using <ThemeProvider> / useTheme pair for example, using "magic" scss rules like [theme: themePrimary, default: #0078d7] (which are pre-processed at runtime) , using window.__themeState__.theme variable
Ultimate goal: have the user upload pictures (less than 16mb so no need to worry about Grid FS), have that picture stored in my database which is Mongodb through Mongoose, and display the picture on the screen using the attribute.
To upload files I use Multer and add it to the database as follows:
newItem.picture.data = Buffer(fs.readFileSync(req.file.path), 'base64');
newItem.picture.contentType = 'image/png';
And it seems to be successfully added to the mongodb. Looks something like this:
how the image appears on mongodb
I'm able to send a get request from my front-end and, when I console.log it, this is what I'm getting: Data after being retreived from database. The question now is, how can I add it to an attribute and show the image on the screen. Thanks!
Edit: question has been marked as too broad by the moderators. Fair enough, I wasn't too sure how to approach it. Since I was able to solve it, this is what my front-end looks like.
componentDidMount() {
const PATH = "http://localhost:8080/apii/items/getitems";
axios.get(PATH)
.then(res => {
let picture64Bit = res.data[0].data.data
picture64Bit = new Buffer(x, 'binary').toString('base64');
this.setState({picture: picture64Bit})
})
.catch(err => console.log(err))
}
The key here is that, 1) res.data[0].data.data is equal to that random list of numbers. I take that convert it back to base64, so it appears exactly as it did in the first picture above from mongodb. Then, displaying it inline in an img attribute is very easy:
<img src = {`data:image/png;base64,${this.state.picture}`} />
There are a couple libraries you could use, but I will arbitrarily select Axios for a demonstration. It sounds good if the images are already in Mongo DB.
Your objective is to get photos from the server to the client, so you need a function to get them on demand. You could also investigate fetch or request.
Axios: https://www.npmjs.com/package/axios
In React, try something like this
async getPhotos() {
const res = await Axios.get('/photos')
console.log('RESPONSE', res)
const photos = res.data
console.log('IMAGES', photos)
this.setState({ photos })
}
Here is a more complete example
import React, { Component } from 'react'
import Axios from 'axios'
class List extends Component {
constructor(props) { // super props allows props to be available
super(props) // inside the constructor
this.state = {
photos : [], // Initialize empty list to assert existence as Array type
// and because we will retrieve a list of jpegs
error: '', // Initialize empty error display
}
}
componentDidMount() {
this.getPhotos() // Do network calls in componentDidMount
}
async getPhotos() {
try {
const res = await Axios.get('/photos')
console.log('RESPONSE', res)
const photos = res.data
console.log('IMAGES', photos)
this.setState({ photos, error: '' })
} catch (e) {
this.setState({ error: `BRUTAL FAILURE: ${e}` })
}
}
render() {
if (error.length) {
return (
<div>{this.state.error}</div>
)
}
if (!photos.length) {
return (
<div>No photos yet</div>
)
}
// Assuming shape { id: 0, caption: 'Cats again', src: 'http://www.com/win.jpg' }
// Make sure to include key prop when using map (for state management)
return (
<ul>
{this.state.photos.map(photo => (
<li key={photo.id} style={{ position: 'relative' }}>
<span>{photo.caption}</span>
<img src={photo.src}
<div
className="overlay"
style={{
position: 'absolute'
width: '100%',
height: '100%',
}}
/>
</li>
))}
</ul>
)
}
}
Citation: In React.js should I make my initial network request in componentWillMount or componentDidMount?
If you want to fetch one more photo after, you should try to think immutably and replace the this.state.photos Array with a duplicate of itself plus the new image pushed onto the end of the array. We will use the spread operator for this to do a shallow copy on the existing photos Array. This will allow React to diff against the two states and efficiently update for the new entry.
const res = await Axios.get('/photo?id=1337')
const photo = res.data
this.setState({
photos: [...photos, photo]
})
Note: the secret trick is to avoid ever doing this.state.photos.push(photo). You must place an illegal sign on setting state like that.
In React, try to consider a way you can get an Object or Array. Once you have it in your mind, throw it into a Component's state. As you progress into Redux, you will end up storing items sometimes in the Redux store. That is too complex and unnecessary to describe now. The photos would be available perhaps as this.props.photos via the Redux Connect Function.
For most other times, a Component's state field is an excellent place to store anything of interest to a Component.
You can imagine it like a holder at the top of the Component.
I have problems with grouped ListView or short group. i get data from webservice already but i don't know how to binding data to html template user behind code.
HTML:
<div class="listLayoutTopHeaderTemplate" data-win-control="WinJS.Binding.Template">
<div class="listLayoutTopHeaderTemplateRoot">
<div data-win-bind="innerHTML: title"></div>
</div>
</div>
<div id="listView"
class="win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemTemplate: select('.smallListIconTextTemplate'),
groupHeaderTemplate: select('.listLayoutTopHeaderTemplate'),
layout: { type: WinJS.UI.ListLayout, groupHeaderPosition: 'top'}}">
</div>
Winjs:
var myData = new WinJS.Binding.List([
{ title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "/images/fruits/60Banana.png" },
{ title: "Lavish Lemon Ice", text: "Sorbet", picture: "/images/fruits/60Lemon.png" },
]);
var grouped = myData.createGrouped(function (item) {
return item.title.toUpperCase().charAt(0);
}, function (item) {
return {
title: item.title.toUpperCase().charAt(0)
};
}, function (left, right) {
return left.charCodeAt(0) - right.charCodeAt(0);
});
listView.winControl.groupDataSource = grouped.groupDataSource;
You need to take whatever data you get from the web service, which is presumably JSON, and transfer that data into a WinJS.Binding.List that you can use as the ListView's data source. In the code you show, this is the myData variable, which you can initialize as empty (instead of using the shown data that's just from a sample). Iterating over your web service data, call the List's add method for each item. If the JSON you want to render already has an array, then parse that out and pass the array to the WinJS.Binding.List constructor.
Either way, you end up with a WinJS.Binding.List populated with the data from the service, which you can then give to the ListView for rendering.
There are fancier ways of working with data sources if you want to avoid obtaining the web service data and copying it all into an in-memory WinJS.Binding.List. For this I'll refer you to Chapter 7 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, specifically the section "Collection Control Data Sources" on page 380.
I have a simple WINJS flipview. There are 5 images loaded from an external json file. All the images load immediately except the first one, and second question is there a simple command to automatically set these to rotate?
So we are using a single page model app. This is a little promo slider I wanted to put in on one page and rotate. I've tried everything including just the demo, but the first item always comes back undefined.
I even tried removing the first image, but the first item always comes back undefined. I've spent a few days on this now with not much luck.
<div id="promoTemplate" data-win-control="WinJS.Binding.Template" style="display: none" >
<div class="overlaidItemTemplate">
<img class="image" data-win-bind="src: picture" />
<div class="overlay">
<h2 class="ItemTitle" data-win-bind="innerText: title"></h2>
</div>
</div>
</div>
<div id="promoFlipView" class="flipView" data-win-control="WinJS.UI.FlipView" data-win-options="{ itemDataSource: ActivityPromoData.bindingList.dataSource, itemTemplate: select('#promoTemplate') }">
</div>
This is connected to the demo example flipview Data
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
(function () {
"use strict";
// This is an array that will be used to drive the FlipView in several
// scenarios. The array contains objects with the following attributes:
//
// type - There are two types that are used:
//
// item -
// The type for simple items. It informs the custom
// renderer that their is a title and picture that needs
// to be rendered.
//
// contentsArray -
// This is used for creating a table of contents. It
// informs the renderer that an array of data is present
// for use in constructing the Table of Contents.
//
// title - The title of a photo to be displayed.
//
// picture - The location of the photo to be displayed.
var array = [
{ type: "item", title: "Cliff", picture: "images/Cliff.jpg" },
{ type: "item", title: "Grapes", picture: "images/Grapes.jpg" },
{ type: "item", title: "Rainier", picture: "images/Rainier.jpg" },
{ type: "item", title: "Sunset", picture: "images/Sunset.jpg" },
{ type: "item", title: "Valley", picture: "images/Valley.jpg" }
];
var bindingList = new WinJS.Binding.List(array);
WinJS.Namespace.define("ActivityPromoData", {
bindingList: bindingList,
array: array
});
var e = ActivityPromoData.bindingList.dataSource;
})();
The original question above here is the FIRST IMAGE BUG FIX: adding this to the onready. This works providing there is no custom animations.
var proxyObject;
proxyObject = new WinJS.Binding.as({
itemTemplate: tutorialTemplate,
customAnimations: false
});
tutorialFlipView.winControl.itemTemplate = tutorialTemplate;
There is not builtin command to rotate. setInternval() can be used for this.
var timerId = setInternal(function()
{
if (flipview.winControl.count - 1 == flipview.winControl.currentPage)
flipview.winControl.currentPage = 0;
else
flipview.winControl.next();
}, slideshowInternal);
// to stop slideshow
clearInterval(timerId);
This assumes no complex transition between pages (for example: KENBURNS). if that is required, it is more involved problem, and it is good to consider using some existing javascript sdk on web and integrate in a custom winjs control. flipview control did not work well when integrating custom page transition animations.
regards image not loading - html/js code snippet will help answer it.
if <img> tag is used and bound it to the http image url, image is not guaranteed to be loaded when flipview shows the page. If number of images are handful, it might be better to download them using winjs.xhr to ensure that they are in cache and then, load the flipview.