Why doesn't Masonry refresh indexes of cells (cellCount) on componentWillReceiveProps method? - react-virtualized

Every next search query starts with previous page height and index count.
What can I do to update the index count in cellRenderer and the height of the page on a new search query?
Next code help to avoid errors, but doesn't solve the problem:
cellRenderer = ({index, key, parent, style}) => {
const image = this.props.images.items[index];
if (!image) {
console.log('!image (empty image with index: ', index, 'style', style);
return;
}
Code: pastebin.com/FB7kzDb4

tl;dr You can fix your demo by doing the following:
// Clear any JIT measurements
cellMeasurerCache.clearAll();
// Reset the position cache
cellPositioner.reset({
columnCount: this.columnCount,
columnWidth: this.columnWidth,
spacer: this.spacer,
});
// Let Masonry know it needs to relayout
this.masonry.clearCellPositions();
Here is an example of me doing the same on the RV demo page. You can see it running here by clicking the "Reset List" button.

Related

How to make a text field link be opened in a new tab, in Velo?

In Wix, I have a text field in a repeater that is used for navigating to other dynamic pages. The link works, but there are two problems with that. First, I have to click two times, not double click, for functioning the link. Second, I want to make the text field act as a button link, I mean be able to right click on that and choose 'open in new tab'. How can I fix these problems in my code?
Here is the code
// Navigating to related dynaic page
import wixLocation from 'wix-location';
export function ndText_click(event) {
$w("#repeater1").onItemReady(($item, itemData, index) => {
$item("#nText").onClick((event) => {
let postTypeValue = itemData.pType
wixData.query("Collection1").eq("_id", itemData._id)
.find()
.then(results => {
let item = results.items[0];
let pIDValue = item.postId;
if (postTypeValue == "R") {
wixLocation.to('/re/' + postIDValue);
} else if (postTypeValue == "L") {
wixLocation.to('/lo/' + postIDValue);
}
})
});
})
}
I suggest trying to use a button instead of the text element. You can usually style the button so it looks the same as the text element you already have. Then instead of setting the onClick, try setting the button's link and target properties.

Dynamic Table updates "too late" ReactJS

my problem is, that I have a table which should update everytime when the user chooses something from a dropdown component. The problem now is that my table updates "too late" in the frontend. So when the user chooses an option for the first time nothing will happen. Then when the user chooses an option for the second time from the dropdown component, the table will show the data from the option he has picked before. If the user chooses an option for the 3rd time, the table will show the data from the second one and so on.
So how can I fix this? I work with ReactJS and Semantic UI
My Code:
This renders the Row for the existing data
renderTableData() {
return this.state.songs.map((song, index) => {
const { id, nr, songname, link } = song
return (
<Table.Row key={id}>
<Table.Cell>{nr}</Table.Cell>
<Table.Cell>{songname}</Table.Cell>
<Table.Cell>{link}</Table.Cell>
</Table.Row>
)
})
}
The Code in the main render() function of React (Its shown correctly, expect that the data is "outdated":
`<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell width={1}>Nr</Table.HeaderCell>
<Table.HeaderCell width={2}>Songname</Table.HeaderCell>
<Table.HeaderCell width={1}>Link</Table.HeaderCell>
</Table.Row>
</Table.Header>
{this.renderTableData()}
</Table>`
The code when the option from the dropdown gets changed:
onChangeDropdown(e) {
this.setState({game: e.target.textContent}, ()=>{
this.state.songs.length = 0;
for(var i = 0; i< this.state.musicData.length;i++){
if(this.state.musicData[i].game == this.state.game){
for(var j = 0; j<this.state.musicData[i].songs.length;j++){
this.state.songs.push({id: j+1, nr: j+1, songname: this.state.musicData[i].songs[j].name, link: this.state.musicData[i].songs[j].link})
}
break;
}
}
this.renderTableData()
})
}
The game variable in this.setState is correct and also the for-loop works as expected when the user changes the dropdown option, I already checked it with the debugger
I hope you can help me out there, ty
is not that is updating too late, is that you are mutating the state without using setState so React doesn't know what changed, you should refactor your code to always use setState to update the state, not push, something like this:
onChangeDropdown(e) {
this.setState((currentState) => {
const newSongs = [];
const game = e.target.textContent;
musicData.forEach((data) => {
if (data.game === game) {
musicData.songs.forEach((song, index) => {
newSongs.push({
id: index + 1,
nr: index + 1,
songname: song.name,
link: song.link,
});
});
}
});
return {
...currentState,
game,
songs: newSongs,
};
});
}
I changed your for loops to use forEach, less complexity, easier to read
Here is what I did:
create a empty array to store the selected songs (newSongs)
loop all the music data and then loop all the songs inside each item in music data
add the songs from the selected game into newSongs
return newSongs + game to update the selected game, ...currentState is to preserve the other parts of the state between changes
So every time the dropodown changes, I create a new array and run the logic
The setState callback can return an object to replace whole state, so before that you can do any calculation you need to.
Updating the state in React is asyncronous, that's one of the reasons you can't mutate the state directly and need to use setState any time you need to update it

How do I scroll right in puppeteer 5.1.0 using mouse.wheel?

I have a grid on this page. The last column is off screen to the right. I want to read the header cell text.
I saw this snippet at
https://pptr.dev/#?product=Puppeteer&version=v5.1.0&show=api-class-mouse
So I ran the code below in cucumber/puppeteer. There were no errors but nothing happened in the browser. So how do I scroll to the right using this feature or any other way.
I can't use querySelector... to get the header because the selectors don't exist until the column is visible. Please advise.
async function scrollRight() {
await this.page.mouse.wheel({ deltaX: 2500 })
}
Your scrollRight() function should have a part where the mouse hovers over the right column. In the linked example there is a page.mouse.move(x,y) which should be applied in your case as well.
To get the X Y coordinates of your column you can use elementHandle.boundingBox puppeteer function. With a simple formula you can position the cursor exactly to the center of the column.
E.g.:
async function scrollRight() {
const elem = await page.$('.last-column');
const boundingBox = await elem.boundingBox();
await page.mouse.move(
boundingBox.x + boundingBox.width / 2, // x
boundingBox.y + boundingBox.height / 2 // y
);
await page.mouse.wheel({ deltaX: 2500 });
}
To check visibility of the column you can use page.waitForSelector's visible: true option, which waits for the element to be visible, i.e. to not have display: none or visibility: hidden CSS properties. It defaults to false, so if it caused problems in your use case that it was not visible it may help.
await page.waitForSelector('.last-column', {
visible: true,
});

React Native: Reach-Navigation and Pouch-DB - db.put not done before "refresh" callback is run

Relative newbie; forgive me if my etiquette and form here aren't great. I'm open to feedback.
I have used create-react-native-app to create an application using PouchDB (which I believe ultimately uses AsyncStorage) to store a list of "items" (basically).
Within a TabNavigator (main app) I have a StackNavigator ("List screen") for the relevant portion of the app. It looks to the DB and queries for the items and then I .map() over each returned record to generate custom ListView-like components dynamically. If there are no records, it alternately displays a prompt telling the user so. In either case, there is an "Add Item" TouchableOpacity that takes them to a screen where they an add a new item (for which they are taken to an "Add" screen).
When navigating back from the "Add" screen I'm using a pattern discussed quite a bit here on SO in which I've passed a "refresh" function as a navigation param. Once the user uses a button on the "Add" screen to "save" the changes, it then does a db.post() and adds them item, runs the "refresh" function on the "List screen" and then navigates back like so:
<TouchableOpacity
style={styles.myButton}
onPress={() => {
if (this.state.itemBrand == '') {
Alert.alert(
'Missing Information',
'Please be sure to select a Brand',
[
{text: 'OK', onPress: () =>
console.log('OK pressed on AddItemScreen')},
],
{ cancelable: false }
)
} else {
this.createItem();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsScreen');
}
}
}
>
And all of this works fine. The "refresh" function (passed as onGoBack param) works fine... for this screen. The database is called with the query, the new entry is found and the components for the item renders up like a charm.
Each of the rendered ListItem-like components on the "List screen" contains a react-native-slideout with an "Edit" option. An onPress for these will send the user to an "Item Details" screen, and the selected item's _id from PouchDB is passed as a prop to the "Item Details" screen where loadItem() runs in componentDidMount and does a db.get(id) in the database module. Additional details are shown from a list of "events" property for that _id (which are objects, in an array) which render out into another bunch of ListItem-like components.
The problem arises when either choose to "Add" an event to the list for the item... or Delete it (using another function via [another] slideout for these items. There is a similar backward navigation, called in the same form as above after either of the two functions is called from the "Add Event" screen, this being the "Add" example:
async createEvent() {
var eventData = {
eventName: this.state.eventName.trim(),
eventSponsor: this.state.eventSponsor.trim(),
eventDate: this.state.eventDate,
eventJudge: this.state.eventJudge.trim(),
eventStandings: this.state.eventStandings.trim(),
eventPointsEarned: parseInt(this.state.eventPointsEarned.trim()),
};
var key = this.key;
var rev = this.rev;
await db.createEvent(key, rev, eventData);
}
which calls my "db_ops" module function:
exports.createEvent = function (id, rev, eventData) {
console.log('You called db.createEvent()');
db.get(id)
.then(function(doc) {
var arrWork = doc.events; //assign array of events to working variable
console.log('arrWork is first assigned: ' + arrWork);
arrWork.push(eventData);
console.log('then, arrWork was pushed and became: ' + arrWork);
var arrEvents = arrWork.sort((a,b)=>{
var dateA = new Date(a.eventDate), dateB = new Date(b.eventDate);
return b.eventDate - a.eventDate;
})
doc.events = arrEvents;
return db.put(doc);
})
.then((response) => {
console.log("db.createEvent() response was:\n" +
JSON.stringify(response));
})
.catch(function(err){
console.log("Error in db.createEvent():\n" + err);
});
}
After which the "Add Event" screen's button fires the above in similar sequence to the first, just before navigating back:
this.createEvent();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsDetails');
The "refresh" function looks like so (also called in componentDidMount):
loadItem() {
console.log('Someone called loadItem() with this.itemID of ' + this.itemID);
var id = this.itemID;
let totalWon = 0;
db.loadItem(id)
.then((item) => {
console.log('[LOAD ITEM] got back data of:\n' + JSON.stringify(item));
this.setState({objItem: item, events: item.events});
if (this.state.events.length != 0) { this.setState({itemLoaded: true});
this.state.events.map(function(event) {
totalWon += parseInt(event.eventPointsEarned);
console.log('totalWon is ' + totalWon + ' with ' +
event.eventPointsEarned + ' having been added.');
});
};
this.setState({totalWon: totalWon});
})
.catch((err) => {
console.log('db.loadItem() error: ' + err);
this.setState({itemLoaded: false});
});
}
I'm at a loss for why the List Screen refreshes when I add an item... but not when I'm doing other async db operations with PouchDB in what I think is similar fashion to modify the object containing the "event" information and then heading back to the Item Details screen.
Am I screwing up with Promise chain someplace? Neglecting behavior of the StackNavigator when navigating deeper?
The only other difference being that I'm manipulating the array in the db function in the non-working case, whereas the others I'm merely creating/posting or deleting/removing the record, etc. before going back to update state on the prior screen.
Edit to add, as per comments, going back to "List screen" and the opening "Item Details" does pull the database data and correctly shows that the update was made.
Further checking I've done also revealed that the console.log in createEvent() to print the response to the db call isn't logging until after some of the other dynamic rendering methods are getting called on the "Item Details" screen. So it seems as though the prior screen is doing the get() that loadItem() calls before the Promise chain in createEvent() is resolving. Whether the larger issue is due to state management is still unclear -- though it would make sense in some respects -- to me as this could be happening regardless of whether I've called my onGoBack() function.
Edit/bump: I’ve tried to put async/await to use in various places in both the db_ops module on the db.get() and the component-side loadItem() which calls it. There’s something in the timing of these that just doesn’t jive and I am just totally stuck here. Aside from trying out redux (which I think is overkill in this particular case), any ideas?
There is nothing to do with PDB or navigation, it's about how you manage outer changes in your depending (already mounted in Navigator since they are in history - it's important to understand - so componentDidMount isn't enough) components. If you don't use global state redux-alike management (as I do) the only way to let know depending component that it should update is passing corresponding props and checking if they were changed.
Like so:
//root.js
refreshEvents = ()=> { //pass it to DeleteView via screenProps
this.setState({time2refreshEvents: +new Date()}) //pass time2refreshEvents to EventList via screenProps
}
//DeleteView.js
//delete button...
onPress={db.deleteThing(thingID).then(()=> this.props.screenProps.refreshEvents())}
//EventList.js
...
constructor(props) {
super(props);
this.state = {
events: [],
noEvents: false,
ready: false,
time2refreshEvents: this.props.screenProps.time2refreshEvents,
}
}
static getDerivedStateFromProps(nextProps, currentState) {
if (nextProps.screenProps.time2refreshEvents !== currentState.time2refreshEvents ) {
return {time2refreshEvents : nextProps.screenProps.time2refreshEvents }
} else {
return null
}
}
componentDidMount() {
this._getEvents()
}
componentDidUpdate(prevProps, prevState) {
if (this.state.time2refreshEvents !== prevState.time2refreshEvents) {
this._getEvents()
}
}
_getEvents = ()=> {
//do stuff querying db and updating your list with actual data
}

error on a dojo grid when adding new item to the store

I'm stuck with a problem on a dojo grid when adding new item to the store.
I've got :
a dojox/grid/EnhancedGrid containing articles
a tabcontainer where the tabs represent article's family.
Each time I choose a tab , it filters the grid to display that family, so far everything work fine.
But I've a button that allows to add a new article to the grid through a new window.
If the grid is not filtered no problem , but if i've got a tab selected I get the error:
grid assertion failed in itemwritestore
Same error on FF and IE, I search internet for that error but i didn't find anything revelant.
My code if its helps ...
var grid=parent.registry.byId('lagrid');
var items=lagrid.store._arrayOfAllItems;
var item=items[e.rowIndex];
var lestab=parent.registry.byId( 'TabContainerRayon');
var tabsel=lestab.selectedChildWidget.id
var ongletR=tabsel.substring(1,tabsel.length);
if (grid)
{
var storeParent=grid.store;
var itemsParent=storeParent._arrayOfAllItems;
for (i=0 ; i< itemsParent.length ; i++)
{
if (itemsParent[i].col17==idLigne)
{
alert("Article déjà présent");
return false;
}
}
var myNewItem = {
id: grid.rowCount+1,
col2:trim(lagrid.store.getValue(lagrid.getItem(e.rowIndex),"Col5")),
col3:undefined,
col4:undefined,
col5:trim(lagrid.store.getValue(lagrid.getItem(e.rowIndex),"Col6")),
col6:trim(lagrid.store.getValue(lagrid.getItem(e.rowIndex),"Col8")),
col7:undefined,
col8: undefined,
col9: undefined,
col10: 1,
col11: undefined,
col12:trim(lagrid.store.getValue(lagrid.getItem(tabInd[0]),"Col1")),
col13:trim(lagrid.store.getValue(lagrid.getItem(e.rowIndex),"Col2")),
col14:'' ,
col15: ongletR,
col16:"<img src='/" + CheminBase + "/pictures.png?OpenImageResource' border=0>",
col17:idLigne ,
col18:trim(lagrid.store.getValue(lagrid.getItem(e.rowIndex),"Col9"))
};
parent.PctPrixTolere.push(parseInt(lagrid.store.getValue(lagrid.getItem(e.rowIndex),"Col7")));
parent.PresenceReleve.push("0");
}
// ajoute l'item dans le store
grid.store.newItem(myNewItem);
grid.store.save();
parent.registry.byId('external').hide();
Thanks for your help
ok I finally find my mistake thanks to ie debugger :)
in fact I was using grid.rowCount+1 to identify my new item, but if I click onto a tab, I have always less row than the store has => same id than an existing row => assertion failed. I changed that to grid.store._arrayOfAllItems.length and it works fine :)

Resources