So I have a table that I'm currently lazy loading, and I want to cache the data of the 5 most current pages. When a user clicks the forward or backward button, I will send the pageNumber that they are going to, and the direction they are going(prev or next).
In my getData function, I will check if the page they are going to exists in a cachedData array or not. If yes, then I will dispatch that data without making an api call. If no, I will see the direction they are going to. For example, if they are going 1 -> 5, then page 6, I will remove page 1 from the cachedData array, then push page 6 in, and if they are going backward 6 -> 1, I will remove page 6, cuz it's the farthest from current page, and push page 1 instead. Below are the implementations:
const cachedData = [];
async function getData({ payload }) {
const { pageNumber, ...currentPageInfo } = payload;
const cachedPage = cachedData.find(page => page.pageNumber === pageNumber);
if (!cachedPage) {
const response = await fetchData(currentPageInfo);
if (response) {
if (cachedData.length >= 5) {
if (currentPageInfo.direction === "next") cachedData.shift();
cachedData.pop();
}
const { pageInfo, dataList } = response.data;
cachedData.push({
dataList,
pageNumber,
pageInfo,
});
cachedData.sort((x, y) => x.pageNumber - y.pageNumber);
dispatch(getDataSuccess({ res: dataList, pageInfo }));
}
} else {
dispatch(
getDataSuccess({
res: cachedPage.dataList,
pageInfo: cachedPage.pageInfo,
})
);
}
}
Currently it's working, but I'm not sure I'm on the right track. Between the shifting, sorting and finding, the performance aren't very good. The logic is weird because we are using our own way of pagination, and not the usual skip and take approach. But the important thing is whether anything can be improved in this code. Thank you very much.
Related
The project aims to study a new social media:
https://booyah.live/
My needs are:
1 - Collect data from profiles that follow a specific profile.
2 - My account use this data to follow the collected profiles.
3 - Among other possible options, also unfollow the profiles I follow.
The problem found in the current script:
The profile data in theory is being collected, the script runs perfectly until the end, but for some reason I can't specify, instead of following all the collected profiles, it only follows the base profile.
For example:
I want to follow all 250 profiles that follow the ID 123456
I activate the booyahGetAccounts(123456); script
In theory the end result would be my account following 250 profiles
But the end result I end up following only the 123456 profile, so the count of people I'm following is 1
Complete Project Script:
const csrf = 'MY_CSRF_TOKEN';
async function booyahGetAccounts(uid, type = 'followers', follow = 1) {
if (typeof uid !== 'undefined' && !isNaN(uid)) {
const loggedInUserID = window.localStorage?.loggedUID;
if (uid === 0) uid = loggedInUserID;
const unfollow = follow === -1;
if (unfollow) follow = 1;
if (loggedInUserID) {
if (csrf) {
async function getUserData(uid) {
const response = await fetch(`https://booyah.live/api/v3/users/${uid}`),
data = await response.json();
return data.user;
}
const loggedInUserData = await getUserData(loggedInUserID),
targetUserData = await getUserData(uid),
followUser = uid => fetch(`https://booyah.live/api/v3/users/${loggedInUserID}/followings`, { method: (unfollow ? 'DELETE' : 'POST'), headers: { 'X-CSRF-Token': csrf }, body: JSON.stringify({ followee_uid: uid, source: 43 }) }),
logSep = (data = '', usePad = 0) => typeof data === 'string' && usePad ? console.log((data ? data + ' ' : '').padEnd(50, '━')) : console.log('━'.repeat(50),data,'━'.repeat(50));
async function getList(uid, type, follow) {
const isLoggedInUser = uid === loggedInUserID;
if (isLoggedInUser && follow && !unfollow && type === 'followings') {
follow = 0;
console.warn('You alredy follow your followings. `follow` mode switched to `false`. Followings will be retrieved instead of followed.');
}
const userData = await getUserData(uid),
totalCount = userData[type.slice(0,-1)+'_count'] || 0,
totalCountStrLength = totalCount.toString().length;
if (totalCount) {
let userIDsLength = 0;
const userIDs = [],
nickname = userData.nickname,
nicknameStr = `${nickname ? ` of ${nickname}'s ${type}` : ''}`,
alreadyFollowedStr = uid => `User ID ${uid} already followed by ${loggedInUserData.nickname} (Account #${loggedInUserID})`;
async function followerFetch(cursor = 0) {
const fetched = [];
await fetch(`https://booyah.live/api/v3/users/${uid}/${type}?cursor=${cursor}&count=100`).then(res => res.json()).then(data => {
const list = data[type.slice(0,-1)+'_list'];
if (list?.length) fetched.push(...list.map(e => e.uid));
if (fetched.length) {
userIDs.push(...fetched);
userIDsLength += fetched.length;
if (follow) followUser(uid);
console.log(`${userIDsLength.toString().padStart(totalCountStrLength)} (${(userIDsLength / totalCount * 100).toFixed(4)}%)${nicknameStr} ${follow ? 'followed' : 'retrieved'}`);
if (fetched.length === 100) {
followerFetch(data.cursor);
} else {
console.log(`END REACHED. ${userIDsLength} accounts ${follow ? 'followed' : 'retrieved'}.`);
if (!follow) logSep(targetList);
}
}
});
}
await followerFetch();
return userIDs;
} else {
console.log(`This account has no ${type}.`);
}
}
logSep(`${follow ? 'Following' : 'Retrieving'} ${targetUserData.nickname}'s ${type}`, 1);
const targetList = await getList(uid, type, follow);
} else {
console.error('Missing CSRF token. Retrieve your CSRF token from the Network tab in your inspector by clicking into the Network tab item named "bug-report-claims" and then scrolling down in the associated details window to where you see "x-csrf-token". Copy its value and store it into a variable named "csrf" which this function will reference when you execute it.');
}
} else {
console.error('You do not appear to be logged in. Please log in and try again.');
}
} else {
console.error('UID not passed. Pass the UID of the profile you are targeting to this function.');
}
}
This current question is a continuation of that answer from the link:
Collect the full list of buttons to follow without having to scroll the page (DevTools Google Chrome)
Since I can't offer more bounty on that question, I created this one to offer the new bounty to anyone who can fix the bug and make the script work.
Access account on Booyah website to use for tests:
Access by google:
User: teststackoverflowbooyah#gmail.com
Password: quartodemilha
I have to admit that it is really hard to read your code, I spent a lesser amount of time rewriting everything from scratch.
Stated that we need a code piece to be cut/pasted in the JavaScript console of web browsers able to store some data (i.e. expiration of followings and permanent followings) we need some considerations.
We can consider expiration of followings as volatile data: something that if lost can be reset to 1 day later from when we loose this data. window.localStorage is a perfect candidate to store these kind of data. If we change web browser the only drawback is that we loose the expiration of followings and we can tolerate to reset them to 1 day later from when we change browser.
While to store the list of permanent followings we need a permanent store even if we change web browser. The best idea that came to my mind is to create an alternative account with which to follow the users we never want to stop following. In my code I used uid 3186068 (a random user), once you have created your own alternative account, just replace the first line of the code block with its uid.
Another thing we need to take care is error handling: API could always have errors. The approach I chosen is to write myFetch which, in case of errors, retries twice the same call; if the error persists, probably we are facing a temporary booyah.live outage. Probably we just need to retry a bit later.
To try to provide a comfortable interface, the code blocks gathers the uid from window.location: to follow the followers of users, just cut/paste the code block on tabs opened on their profiles. For example I run the code from a tab open on https://booyah.live/studio/123456?source=44.
Last, to unfollow users the clean function is called 5 minutes later we paste the code (to not conflict with calls to follow followers) and than is executed one hour later it finishes its job. It is written to access the localStorage in an atomic way, so you can have many of them running simultaneously on different tabs of the same browser, you can not care about it. The only thing you need to take care it that when the window.location changes, all the JavaScript events in the tab are reset; so I suggest to keep a tab open on the home page, paste the code block on it, and forget about this tab; it will be the tab responsible of unfollowing users. Then open other tabs to do what you need, when you hit a user you want to follow the followers, paste the block on it, wait the job is finished and continue to use the tab normally.
// The account we use to store followings
const followingsUID = 3186068;
// Gather the loggedUID from window.localStorage
const { loggedUID } = window.localStorage;
// Gather the CSRF-Token from the cookies
const csrf = document.cookie.split("; ").reduce((ret, _) => (_.startsWith("session_key=") ? _.substr(12) : ret), null);
// APIs could have errors, let's do some retries
async function myFetch(url, options, attempt = 0) {
try {
const res = await fetch("https://booyah.live/api/v3/" + url, options);
const ret = await res.json();
return ret;
} catch(e) {
// After too many consecutive errors, let's abort: we need to retry later
if(attempt === 3) throw e;
return myFetch(url, option, attempt + 1);
}
}
function expire(uid, add = true) {
const { followingsExpire } = window.localStorage;
let expires = {};
try {
// Get and parse followingsExpire from localStorage
expires = JSON.parse(followingsExpire);
} catch(e) {
// In case of error (ex. new browsers) simply init to empty
window.localStorage.followingsExpire = "{}";
}
if(! uid) return expires;
// Set expire after 1 day
if(add) expires[uid] = new Date().getTime() + 3600 * 24 * 1000;
else delete expires[uid];
window.localStorage.followingsExpire = JSON.stringify(expires);
}
async function clean() {
try {
const expires = expire();
const now = new Date().getTime();
for(const uid in expires) {
if(expires[uid] < now) {
await followUser(parseInt(uid), false);
expire(uid, false);
}
}
} catch(e) {}
// Repeat clean in an hour
window.setTimeout(clean, 3600 * 1000);
}
async function fetchFollow(uid, type = "followers", from = 0) {
const { cursor, follower_list, following_list } = await myFetch(`users/${uid}/${type}?cursor=${from}&count=50`);
const got = (type === "followers" ? follower_list : following_list).map(_ => _.uid);
const others = cursor ? await fetchFollow(uid, type, cursor) : [];
return [...got, ...others];
}
async function followUser(uid, follow = true) {
console.log(`${follow ? "F" : "Unf"}ollowing ${uid}...`);
return myFetch(`users/${loggedUID}/followings`, {
method: follow ? "POST" : "DELETE",
headers: { "X-CSRF-Token": csrf },
body: JSON.stringify({ followee_uid: uid, source: 43 })
});
}
async function doAll() {
if(! loggedUID) throw new Error("Can't get 'loggedUID' from localStorage: try to login again");
if(! csrf) throw new Error("Can't get session token from cookies: try to login again");
console.log("Fetching current followings...");
const currentFollowings = await fetchFollow(loggedUID, "followings");
console.log("Fetching permanent followings...");
const permanentFollowings = await fetchFollow(followingsUID, "followings");
console.log("Syncing permanent followings...");
for(const uid of permanentFollowings) {
expire(uid, false);
if(currentFollowings.indexOf(uid) === -1) {
await followUser(uid);
currentFollowings.push(uid);
}
}
// Sync followingsExpire in localStorage
for(const uid of currentFollowings) if(permanentFollowings.indexOf(uid) === -1) expire(uid);
// Call first clean task in 5 minutes
window.setTimeout(clean, 300 * 1000);
// Gather uid from window.location
const match = /\/studio\/(\d+)/.exec(window.location.pathname);
if(match) {
console.log("Fetching this user followers...");
const followings = await fetchFollow(parseInt(match[1]));
for(const uid of followings) {
if(currentFollowings.indexOf(uid) === -1) {
await followUser(uid);
expire(uid);
}
}
}
return "Done";
}
await doAll();
The problem: I strongly suspect a booyah.live API bug
To test my code I run it from https://booyah.live/studio/123456?source=44.
If I run it multiple times I continue to get following output:
Fetching current followings...
Fetching permanent followings...
Syncing permanent followings...
Following 1801775...
Following 143823...
Following 137017...
Fetching this user followers...
Following 16884042...
Following 16166724...
There is bug somewhere! The expected output for subsequent executions in the same tab would be:
Fetching current followings...
Fetching permanent followings...
Syncing permanent followings...
Fetching this user followers...
After seeking the bug in my code without success, I checked booyah.live APIs: if I navigate following URLs (the uids are the ones the code continue to follow in subsequent executions)
https://booyah.live/studio/1801775
https://booyah.live/studio/143823
https://booyah.live/studio/137017
https://booyah.live/studio/16884042
https://booyah.live/studio/16166724
I can clearly see I follow them, but if I navigate https://booyah.live/following (the list of users I follow) I can't find them, neither if I scroll the page till the end.
Since I do exactly the same calls the website does, I strongly suspect the bug is in booyah.live APIs, exactly in the way they handle the cursor parameter.
I suggest you to open a support ticket to booyah.live support team. You could use the test account you provided us: I already provided you the details to do that. ;)
I am working on a small project, and I have a hard time reaching what I need. I've already requested some help on another matter about this project, but I'm at a loss again, I think this time because I've bitten a bit more than I could chew.
So here goes : I'm using the Musicbrainz API to retrieve informations for a specific track using it's id (it's length, release date, artists, etc ...).
But I'm trying to also show below the details of the track the covers of all the releases where the track is present. This require new requests for each of the release. I manage to retrieve the URLs of the images that I need and push them into an array that I then map / join to add some <img> element so that it can render in the HTML.
However, my issue is that when I click on the button to show more details about a specific track, which in turn fires the function controlTrackDetail in my controller (I've tried to implement some basic MVC architecture as practice ...), the first part of the "rendering" (the general informations from TrackView.render) is fine, but the second part (the covers) is not. I gathered that when I call my CoverView.renderCovers method, the array used as a parameter is still empty, and as such, nothing happens. If I don't empty the array, and click again on my button, it does show all my covers, but only because the URL are those of the previous call to controlTrackDetail.
If anyone has any idea as to how I could tackle this, and only render the covers after all the request in the forEach loop unqReleaseCoversUrl(mbid.id) of API calls is done, that would help me plenty. Below, you'll find the "important" snippets of code to (hopefully) understand my issue.
GET_JSON function (race between a fetch and timeout)
export const GET_JSON = async function (url) {
try {
const res = await Promise.race([
fetch(url),
timeout(CONSTANTS.TIMEOUT_SEC),
]);
const data = await res.json();
if (!res.ok) throw new Error(`${data.message} (${res.status})`);
return data;
} catch (err) {
throw err;
}
};
The model part, where I create the loadTrackDetails function to recover the informations I need.
import { CONSTANTS } from "./config.js";
import { CONVERT_MILLIS_TO_MINS_SECONDS } from "./helpers.js";
import { GET_JSON } from "./helpers.js";
import { SHORTEN_STRING } from "./helpers.js";
import { CONSTRUCT_URL_PART } from "./helpers.js";
/*
https://musicbrainz.org/ws/2/recording/738920d3-c6e6-41c7-b504-57761bb625fd?inc=genres+artists+ratings+releases&fmt=json
loadTrackDetail("738920d3-c6e6-41c7-b504-57761bb625fd");
*/
export const details = {
trackDetails: {},
artistDetails: {},
releaseDetails: {},
coverUrlArray: [],
};
export const loadTrackDetail = async function (id) {
try {
const trackData = await GET_JSON(
encodeURI(
`${CONSTANTS.API_URL}${id}?inc=genres+artists+ratings+releases&fmt=json`
)
);
details.trackDetails = {
trackTitle: trackData.title ?? "No title provided",
trackID: trackData.id,
trackReleaseDate: trackData["first-release-date"] ?? "No date provided",
trackLength: trackData.length
? CONVERT_MILLIS_TO_MINS_SECONDS(trackData.length)
: "No duration provided",
trackArtists: trackData["artist-credit"].length
? trackData["artist-credit"]
: "No information on artists",
trackReleasesBase: trackData["releases"].length
? trackData["releases"]
: "No information on releases",
trackReleasesCleanOne: trackData["releases"].length
? trackData["releases"].map((release) => ({
id: release.id,
title: release.title,
}))
: "No information on releases",
trackGenres: trackData["genres"].length
? trackData["genres"]
: "No information on genres",
trackRating: trackData.rating.value ?? "No rating yet",
};
if (details.trackDetails.trackReleasesCleanOne.length > 0) {
details.trackDetails.trackReleasesCleanOne.forEach((mbid) =>
unqReleaseCoversUrl(mbid.id)
);
}
details.coverUrlArray = details.coverUrlArray.filter(function (element) {
return element !== undefined;
});
console.log(details.coverUrlArray);
} catch (err) {
throw err;
}
};
/*
*/
export const unqReleaseCoversUrl = async function (mbid) {
try {
const coverData = await GET_JSON(
encodeURI(`${CONSTANTS.COVER_API_URL}${mbid}`)
);
console.log(coverData.images);
coverData.images.forEach((image) => {
image.thumbnails["500"]
? details.coverUrlArray.push(image.thumbnails["500"])
: null;
});
} catch (err) {
throw err;
}
};
And finally, the controller part, fired on click on a button
const controlTrackDetail = async function (trackID) {
try {
TrackView.renderSpinner();
await detailsModel.loadTrackDetail(trackID);
// 2) Rendering Recipe
TrackView.render(detailsModel.details.trackDetails);
CoverView.renderSpinner();
CoverView.renderCovers(detailsModel.details.coverUrlArray);
} catch (err) {
console.log(err);
}
};
Like I said, this works ... fine (though I guess it's not really clean ...), aside from the fact that I'm rendering my covers early, and I'm just not sure how to delay it until the URLs are pushed into the arrays.
Thanks for reading me !
PS : here are a few pictures to understand.
On first click, what I see (nothing appears below Cover Arts, where the rendering of the covers should happen) :
After a few milliseconds, the array is filled with the URLs I need :
And when I go back to the results, and click on any button without emptying the array, it is used to render the covers using the URLs that I pushed the first time around (and this array will carry on growing !)
Created a simple react-admin application that pulls from a custom rest api. First page is displayed (default 10 per page. Click the Next button and nothing happens (still sends page=1 to the api). Click a second time and the page advances to page 2 (page=2), as expected. Click the third time and goes back to page 1 (page=1).
Then, if you click a fourth time, it goes page 2, then click again, goes to page 3, then click again, goes back to page 1. It continues with this pattern, each round, getting one page further before going back to page.
I'm able to get the correct results when calling the custom API outside of the react-admin app. I created a custom dataProvider to communicate with the API and maybe there's a problem with the getList function, but I can definitely see the page number passed into this function and it lines up with the odd results (page 1, then 1, 2, 1, then 1, 2, 3, 1, etc. The custom API expects the following query string for pagination: ?limit=10&page=1&orderBy=id&orderDir=ASC
The original react-admin tutorial returns 10 records. When I set the page limit to 5, it does seem to work OK (advances to page 2 on the first click of Next), but without more records, it's hard to test it completely. But my guess is it would work, since it is most certainly a problem with my code or the API (although, as I said, the API works outside the react app).
Here's my getList function:
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const tokens = localStorage.getItem('tokens');
const objToken = JSON.parse(tokens);
options.user = {
authenticated: true,
token: `Bearer ${objToken.accessToken}`
};
return fetchUtils.fetchJson(url, options);
};
export default {
getList: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const { q } = params.filter;
// Pagination and sort
let query = `limit=${perPage}&page=${page}&orderBy=${field}&orderDir=${order}`;
// Filter?
let useResource = '';
let useFilter = '';
if (q == null) {
// No filter: Use <resource>/ url
useResource = resource;
} else {
// Filter: Use append url with /find
useResource = `${resource}/find`;
useFilter = q;
console.log('useFilter: ', useFilter)
query += `&searchText=${useFilter}`;
}
const url = `${apiUrl}/${useResource}?${query}`;
return httpClient(url)
.then(({ json }) => ({
data: json.results,
total: json.totalRows,
}));
}, ...
Here's a screen shot of issue:
EDIT:
It looks like the correct query string is being sent but immediately after the first Next page click (page=2), page=1 is automatically sent again, returning to page one. This seems to be the case with subsequent Next clicks, as well. Thanks for helping out a newbie. But I just can't figure out why extra calls are being made returning to page 1.
Fixed in react-admin 3.4.3.
I updated using npm update and pagination works correctly.
I have exactly behavor with react 4.x.x
What i was expecting:
Going to next page when cliking on next, with react-admin 3.19 this is how my application worked
What happened instead:
when you click on the next page, pagination resets to 1 !
also, it does not take into account the pagination that I define.
on chrome default perPage is 5, even when i set it 10.
chrome_pagination_issue
on firefox default perPage=10, but i have the same issue
firefox_pagination_issue
Other information:
getList: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
console.log(params);
const query = {
...fetchUtils.flattenObject(params.filter),
_sort: field,
_order: order,
_start: (page - 1) * perPage,
_end: page * perPage,
_resource:resource
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => {
if (!json.hasOwnProperty('totalElements')) {
throw new Error(
"The numberOfElements property must be must be present in the Json response"
);
}
return {
data: json.content,
total: parseInt(json.totalElements,10)
};
});
}
#4658
Environment
React-admin version: 4.0.1 , 4.0.2
React version:18
Strict mode disabled
Browser: chrome, firefox
My backend is spring boot rest api
I see that someone has given me a minus 1. I am a 55 year old mother who has no experience. I have many skills but this is not one of them. I am absolutely desperate and have bust myself to get this far. If you cannot help, I accept that, but please do not be negative towards me. I am now crying. Some encouragement would be much appreciated.
I have a page which displays items from a database on a repeater. The code searches the items using several drop down filters, which are populated from the database. Intermittently, seemingly randomly (no pattern is emerging despite extensive testing) the code is failing to populate random drop down filters (one or more of the drop down filters show the default settings rather than those self populated from the database). I discovered this by either repeatedly visiting the page or by repeatedly refreshing the page. Often the code works, then every 3 or 4 times, one or more of the drop down filters shows its default settings rather than those self populated from the database (then the next time it goes wrong, it might be the same or a different one or set of filters which do not work)
This is the code. On this page, there are 3 drop down filters but I have several pages like this, each displaying and searching a different database, with up to 10 drop down filters on each page, and they all have this intermittent problem...
import wixData from "wix-data";
$w.onReady(function () {
$w('#iTitle')
$w('#iCounty')
$w('#iGeog')
$w('#dataset1')
$w('#text102')
});
let lastFilterTitle;
let lastFilterCounty;
let lastFilterGeog;
export function iTitle_change(event, $w) {
filter($w('#iTitle').value, lastFilterCounty, lastFilterGeog);
}
export function iCounty_change(event, $w) {
filter(lastFilterTitle, $w('#iCounty').value, lastFilterGeog);
}
export function iGeog_change(event, $w) {
filter(lastFilterTitle, lastFilterCounty, $w('#iGeog').value);
}
function filter(title, county, geog) {
if (lastFilterTitle !== title || lastFilterCounty !== county || lastFilterGeog !== geog) {
let newFilter = wixData.filter();
if (title)
newFilter = newFilter.eq('title', title);
if (county)
newFilter = newFilter.eq('county', county);
if (geog)
newFilter = newFilter.eq('geog', geog);
$w('#dataset1').setFilter(newFilter)
.then(() => {
if ($w('#dataset1').getTotalCount() ===0) {
$w('#text102').show();
}
else {
$w('#text102').hide();
}
})
.catch((err) => {
console.log(err);
});
lastFilterTitle = title;
lastFilterCounty = county;
lastFilterGeog = geog;
}
}
// Run a query that returns all the items in the collection
wixData.query("Psychologists")
// Get the max possible results from the query
.limit(1000)
.ascending("title")
.distinct("title")
.then(results => {
let distinctList = buildOptions(results.items);
// unshift() is like push(), but it prepends an item at the beginning of an array
distinctList.unshift({ "value": '', "label": 'All Psychologists'});
//Call the function that builds the options list from the unique titles
$w("#iTitle").options = distinctList
});
function buildOptions(items) {
return items.map(curr => {
//Use the map method to build the options list in the format {label:uniqueTitle, valueuniqueTitle}
return { label: curr, value: curr };
})
}
// Run a query that returns all the items in the collection
wixData.query("Psychologists")
// Get the max possible results from the query
.limit(1000)
.ascending("county")
.distinct("county")
.then(results => {
let distinctList = buildOptions(results.items);
// unshift() is like push(), but it prepends an item at the beginning of an array
distinctList.unshift({ "value": '', "label": 'All Counties'});
//Call the function that builds the options list from the unique titles
$w("#iCounty").options = distinctList
});
function buildOptions1(items) {
return items.map(curr => {
//Use the map method to build the options list in the format {label:uniqueTitle1, valueuniqueTitle1}
return { label: curr, value: curr };
})
}
// Run a query that returns all the items in the collection
wixData.query("Psychologists")
// Get the max possible results from the query
.limit(1000)
.ascending("geog")
.distinct("geog")
.then(results => {
let distinctList = buildOptions(results.items);
// unshift() is like push(), but it prepends an item at the beginning of an array
distinctList.unshift({ "value": '', "label": 'All Regions'});
//Call the function that builds the options list from the unique titles
$w("#iGeog").options = distinctList
});
function buildOptions2(items) {
return items.map(curr => {
//Use the map method to build the options list in the format {label:uniqueTitle2, valueuniqueTitle2}
return { label: curr, value: curr };
})
}
export function button45_click(event, $w) {
//Add your code for this event here:
filter($w('#iTitle').value='', $w('#iCounty').value='', $w('#iGeog').value='');
}
My experience and knowledge is very limited, so the answer may well be very simple. Any help would be much appreciated as I will have to abandon my project if I can't find a solution.Thank you
I'm trying to use ReactJS on Salesforce but the queries are quite problematic
Take a look at this code, trying to make something cool to get all data but the while isn't stop, don't know why, please help me and feel free to ask me anything
componentWillMount(){
let queryMore = true
let offSet = 0
let contents = []
while(queryMore) {
makeDeferredProvider()
let contentObject = SObjectModel.deferredObject('Content')
let retrieveOptions = {
limit: 10,
where: {
Type__c: { eq: 'Image' }
}
}
offSet > 0 ? retrieveOptions.offset = offSet : ''
let contentPromise = contentObject.retrieve(retrieveOptions)
contentPromise.then( records => {
records.forEach( record => {
let item = {
'name': record.get('Name'),
'id': record.get('Id')
}
contents.push(item)
})
records.length = 10 ? offSet += 10 : queryMore = false
}, error => {
console.log(error)
})
}
this.setState({
contents: contents
})
}
Async React will make this easier in the future, but right now you need to not block rendering while your promise is resolving.
Here's a high-level solution:
Stop using ComponentWillMount. It is being deprecated.
Stop using the while loop. It won't work with your promise. Get it working first then worry about queryMore later. See below.
Add a field in the component state called "loading" and default it to true.
Have a conditional in your render method to load a spinner (or NULL, or something) when "loading" equals true.
In ComponentDidMount*, call your request logic and set state WITHIN the function passed into the .then method after you retrieved all the contents. Make sure to set "loading" to false there.
The conditional in render should then render the contents.
Consider using JSForce (https://jsforce.github.io/) to connect to your object. It already has autoFetch logic included that could replace your queryMore logic.
* Using ComponentDidMount should work but it's probably a bad idea because the component will call out to SF on each render. You should probably use a handler and only call when you don't have the data in state or if you want to refresh for some other reason.