Load WooCommerce data on demand and show it in a DataGrid by Syncfusion - node.js

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

Related

Need to call an api for each key stroke in react, but the response can have thousands of objects

I am using react and axios for frontend, and nextjs with prisma for backend. I have in the database 4000 exercices that contain fitness exercices. I want to create a function where by each key stroke, the api will look for the relevant exercice. I finished creating it, but i have some issues:
The main problem is that the response is delayed from the first keystrokes, because the payload response is tooo large. I created a scrollable UL element to render the elements, because I want to get also the Gif images. So the elements, if the API will find those, will be rendered on the screen.
If I add to each element an on click event, to select the exercice's Id, I get an error "too many re-rendering on the screen".
How can I optimise the function, and how can I solve the error of too many re-render on the screen? Nextjs tells me that it will create an infinite loop....
The frontend looks like this:
const [open, setOpen] = useState(false);
const [keyWord, setKeyWord] = useState('');
const [array, setArray] = useState([]);
const [exerciceId, setExerciceId] = useState('');
// Add exercice
const hadnleAddExercie = async event => {
event.preventDefault();
console.log('exercice added');
}
// Look for exercices
const searchExercices = async event => {
event.preventDefault();
setKeyWord(event.target.value);
const arrayExercices = await getExercicesByKeyWords(keyWord);
setArray(arrayExercices);
console.log(arrayExercices);
}
<div className='flex mt-3 flex-col'>
<input onChange={searchExercices} required placeholder='Search by word...' className='border border-slate-400 p-1 rounded-md flex-1 max-w-sm my-2'/>
<ul className='border border-slate-400 p-1 rounded-md max-w-sm my-2 max-h-52 overflow-scroll'>
{
array.length > 1 && array.map(exercice => (
<li key={exercice.id} className='flex flex-wrap p-2 bg-slate-200 m-2 items-center rounded-md'>
<span><Image className='rounded-xl mr-2' priority width={40} height={40} src={exercice.gifUrl} alt={exercice.name}/></span>
<span>{ exercice.name }</span>
</li>
))
}
</ul>
</div>
The backend Uses prisma and I use the OR clause to look for a word in different rows:
export default async function handler(req, res) {
try {
const param = req.query.slug[0];
console.log(param);
// Get exercices where the two rows contains a single parametter
const exercices = await prisma.exercices.findMany({
where: {
OR: [
{
name: {
contains: param
}
},
{
target: {
contains: param
}
},
{
equipment: {
contains: param
}
}
]
}
});
res.status(200).send(exercices);
}
catch (error) {
console.log(error);
res.status(500).send(error);
}
}
An example can be this:
Only for finding an exercice I used 500mb...
Here are a few ways I can think of to optimize this:
Use pagination and fetch more results as user scrolls down or actually separate it by using pages. You can read more on how to implement pagination in Prisma here.
Add debounce to your search term so it doesn't actually fire on every single keystroke, you could use something like useDebounce.
Use React.memo to prevent the list from being re-rendered every time some state changes, only re-render it when the actual list changes.

How to prevent re-rendering when switching the pages in SolidJS?

I'm struggling with the re-rendering issue in the SolidJS application. I have two routes, Home and Detail. A user can explore items in Home, and click the link on the item name to switch a page to Detail to check out detailed information.
export default function Home() {
const [items, setItems] = createSignal<Item[]>([]);
onMount(async () => {
setItems(
await fetchItemsThroughExpensiveAPI()
);
});
return (
<main>
<For each={items()}>
{(item) => (
<A href={`/item/${item.id}`}>{item.name}</A>
)}
</For>
</main>
);
}
export default function Detail() {
const params = useParams<{ id: string }>();
return (
<main>
// Some detailed information for the item ...
</main>
);
}
At this point, the API(fetchItemsThroughExpensiveAPI) will be called back when the user returns to the Home from Detail. I'm expecting this it is caused by re-rendering. How do I prevent re-rendering Home whenever a user returns to Home from another page to avoid unnecessary API calls?
Use a resource to fetch the data outside the Home component. If you need to fetch the data once during application's life, cache it.
https://www.solidjs.com/docs/latest/api#createresource
Lets make it more clear. There are different patterns to render async data, data that resides in a remote location.
Fetch as you render: In this pattern, data is fetched when the component mounts.
ComponentA below uses this pattern. Whenever it is re-rendered, data will be re-fetched.
Fetch then render: In this pattern, data is fetched outside the component, in one of its parent's scope. When component mounts it can use whatever is currently available, by whatever, I mean the request may not be resolved yet so state will be pending.
Resource API is build to make use of this pattern.
ComponentB below uses this pattern. Since data is fetched outside the component, re-rendering has no effect on it.
import { Accessor, Component, createSignal, Match, Switch } from 'solid-js';
import { render } from 'solid-js/web';
interface State { status: 'pending' | 'resolved' | 'rejected', data?: any, error?: any };
function getData(): Accessor<State> {
const [state, setState] = createSignal<State>({ status: 'pending' });
setTimeout(() => {
setState({ status: 'resolved', data: { name: 'John Doe', age: 30 } });
}, 1000);
return state;
};
const ComponentA = () => {
const state = getData();
return (
<Switch fallback={<div>Not Found</div>}>
<Match when={state().status === 'pending'}>
Loading...
</Match>
<Match when={state().status === 'resolved'}>
{JSON.stringify(state().data)}
</Match>
<Match when={state().status === 'rejected'}>
{JSON.stringify(state().error)}
</Match>
</Switch>
);
};
const ComponentB: Component<{ state: Accessor<State> }> = (props) => {
return (
<Switch fallback={<div>Not Found</div>}>
<Match when={props.state().status === 'pending'}>
Loading...
</Match>
<Match when={props.state().status === 'resolved'}>
{JSON.stringify(props.state().data)}
</Match>
<Match when={props.state().status === 'rejected'}>
{JSON.stringify(props.state().error)}
</Match>
</Switch>
);
};
const App = () => {
const state = getData();
const [show, setShow] = createSignal(false);
const handleClick = () => setShow(prev => !prev);
return (
<div>
{show() && (<ComponentA />)}
{show() && (<ComponentB state={state} />)}
<div><button onclick={handleClick}>Toggle Show Components</button></div>
</div>
)
};
render(() => <App />, document.body);
Here you can see it in action: https://playground.solidjs.com/anonymous/32518df5-9840-48ea-bc03-87f26fecc0f4
Here we simulated an async request using setTimeout. This is very crude implementation to prove a point. If you are going to fetch a remote resource, you should use the Resource API which provides several utilities like automatic re-fecthing when request parameters change.
There are few issues with your implementation. First and foremost, async data in never guaranteed to be received, so you should handle failures.
Since it takes some time to receive the remote data, you have to show the user some indicator of the ongoing request, like a loader.
If API call is an expensive operation, you have to cache the result, rather than forcing the UI not to re-render. In this sense, your approach is very problematic.
Utilize the useEffect hook. When the component is first loaded, the useEffect hook can be used to retrieve the items from the API and store them in a state variable. This way, when the component re-renders, it can access the stored items in the state variable instead of making a new API call.
export default function Home() {
const [items, setItems] = useState<Item[]>([]);
useEffect(() => {
async function fetchData() {
setItems(await fetchItemsThroughExpensiveAPI());
}
fetchData();
}, []);
return (
<main>
<For each={items}>
{(item) => (
<A href={`/item/${item.id}`}>{item.name}</A>
)}
</For>
</main>
);
}

How should I fetch payment intent secret for Stripe Elements in my Next.js app?

I am trying to implement Stripe payments in my Next.js app as described in the guide here: https://stripe.com/docs/payments/quickstart
The guide tells me that in order to use Stripe Elements for my checkout form, I need to know payment intent. It says:
Create PaymentIntent as soon as the page loads
The issue is - our website will not have a separate payments page, the payment form will be displayed inside the modal, which is loaded on every page of the website. That means, I would have to fetch the payment intent for any user who ever visits any page on our website, whether they're planning to purchase the course or not, just so that I could display the payment form inside the modal. That doesn't seem right to me.
Can you give me some advice, let me know if there's a better way to handle this?
Another issue is that this guide tells me that I should pass the fetched payment intent clientSecret as an option to <Elements/> wrapper.
And if I hover on <Elements/> wrapper in my VSCdoe, it tells me:
[...] Render an Elements provider at the root of your React app so that it is available everywhere you need it. [...]
So, does that mean I have to put <Elements/> wrapper into my _app.tsx file? And that means I'd have to fetch the payment intent clientSecret inside of the _app.tsx? So that my app would fetch payment intent secret any time any user ever loads any page on my website?
Again, this seems pretty weird, wouldn't it slow things down, add extra requests and loading time to all my pages, and create a whole bunch of payment intents that are never used?
Render the payment form in a modal in Layout.js and wrap the
entire project in the Layout component
place this code in _app.js
import React, { useEffect, useState } from "react"
import { loadStripe } from "#stripe/stripe-js"
import { Elements } from "#stripe/react-stripe-js"
import Layout from "../components/Layout"
import PaymentModalForm from "../components/PaymentModalForm"
const promise = loadStripe("pk_test_....")
// replace pk_test_... with your publishable key
const API_URL = "http://localhost:8000"
// replace API_URL with your backend server url
const App = ({ Component, pageProps }) => {
const [secret, setSecret] = useState(null)
useEffect(() => {
const fetchSecret = async () => {
const response = await fetch(`${API_URL}/create_intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [{ id: 'adidas boost', quantity: 2}]
})
})
const { client_secret } = await response.json()
setSecret(clientSecret)
}
fetchSecret()
}, [])
const options = {
clientSecret: secret,
appearance: { theme: "stripe"}
}
return (
{secret && (
<Elements stripe={promise} options={options}>
<Layout>
<Component {...pageProps} />
</Layout>
</Elements>
)}
)
}
export default App
Then in your Layout.js, fill in this code
import PaymentModalForm from "../components/PaymentModalForm"
import React, { useEffect, useState } from "react"
const Layout = ({ children }) => {
const [showModal, setShowModal] = useState(false)
const handleClick = () => {
if (showModal) {
setShowModal(false)
} else {
setShowModal(true)
}
}
return (
<div>
<div className="container">
{children}
<button onClick={handleClick}>Show Payment Modal</button>
</div>
{showModal ? (
<div className="modal fade">
<div className="modal-dialog">
<div className="modal-content">
<PaymentModalForm />
</div>
</div>
</div>
) : ( null )}
</div>
)
}
export default Layout
There's more work to be done in PaymentModalForm.js

How to display an image with <img> from Mongoose using React front-end

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.

Leave screen callback React Navigation Tab Navigator

I have a React Navigation Tab Component like this:
const RootNavigator=TabNavigator({
Home:{
screen: Home,
navigationOptions:{
tabBarIcon: ({focused}) => (
<Icon
name={focused? 'ios-home':'ios-home-outline'}
style={{color: '#464646'}}
size={16}
/>
)
}
},
Notifications:{
screen: Notifications,
navigationOptions:{
tabBarIcon: ({focused}) => (
<TabNotifications focused={focused} />
)
}
}, {});
Is there a way to make a callback when leaving a screen?
In this case, I would like to perform a function when I leave the Notifications tab. Such as mark the notifications as seen and remove the badge indicator.
As of now, I am pulling the Notification icon from another component in order to show the number badge.
Thanks in advance.
One option is to use onNavigationStateChange to check the current change of the navigation and do the action you need to clear notifications etc.
onNavigationStateChange(prevState, newState, action)
Function that gets called every time navigation state managed by the
navigator changes. It receives the previous state, the new state of
the navigation and the action that issued state change. By default it
prints state changes to the console.
Another option is to use addListener. This way you can subscribe to willFocus/didFocus or willBlur/didBlur events and do the action you need.
addListener - Subscribe to updates to navigation lifecycle
React Navigation emits events to screen components that subscribe to
them:
willBlur - the screen will be unfocused
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
didBlur - the screen unfocused (if there was a transition, the transition completed)
Example from the docs
const didBlurSubscription = this.props.navigation.addListener(
'didBlur',
payload => {
console.debug('didBlur', payload);
}
);
// Remove the listener when you are done
didBlurSubscription.remove();
// Payload
{
action: { type: 'Navigation/COMPLETE_TRANSITION', key: 'StackRouterRoot' },
context: 'id-1518521010538-2:Navigation/COMPLETE_TRANSITION_Root',
lastState: undefined,
state: undefined,
type: 'didBlur',
};
For those who want a third option, you could use the NavigationEvents component as suggested by the docs to listen to navigations hooks and to whatever you intended to do.
Edit: This is documentation for React Navigation 2.x, which is no
longer actively maintained. For up-to-date documentation, see the
latest version (6.x).
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus',payload)}
onDidFocus={payload => console.log('did focus',payload)}
onWillBlur={payload => console.log('will blur',payload)}
onDidBlur={payload => console.log('did blur',payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;

Resources