Is there a way to access the current view's data in material-table? - material-table

My use case is that when a user filters the table data using search, I'd like to be able to use an external widget to perform actions on each row of that data as it is shown in the table.
Right now I dump all my data into cols={MyData} and sort through data[index] but ideally I'd like to be perform operations with something like currentlyDisplayedTableData[index].
There doesn't seem to be a documented way of doing this so I have no attempt to show, I'm just wondering if someone may have encountered this problem and could show me the light.
re: https://github.com/mbrn/material-table/issues/1124

Just thought I should share another tip, if you just want to intercept/intervene and operate on the currently displayed data before render you can override the component for the table body as Tyler showed in the "issue" link.
But instead of adding a render method, like Tyler did, you can just intercept the props on it's "way down" like this and inject it in the next component (Body, Row, etc.
Note; look for EditRow and other components in https://material-table.com/#/docs/features/component-overriding
<MaterialTable
//...
/**
* be aware when making changes on data that there is a tableData object attached
* rowData: {
* name: 'some name',
* tableData : {id: 3}
* }
*/
components={{
Body: (props) => {
//intervene before rendering table
console.log("tampering with some table data ", props);
console.log(" -- table data looks like this ", props.renderData);
// do stuff..
const myRenderData = props.renderData;
return (
<>
<MTableBody {...props} renderData={myRenderData} />
{/* to show that you will make impact */}
{/* <MTableBody {...props} renderData={[]} /> */}
</>
)
},
Row: (props) => {
//intervene before rendering row
console.log("tampering with some row data ", props);
console.log(" -- row data looks like this ", props.data);
console.log(" -- row table data looks like this ", props.data.tableData);
// do stuff..
const myRenderData = props.data;
return (
<>
<MTableBodyRow {...props} data={myRenderData} />
</>
)
}
}}

#imjared
I found this thread, via the issue, today and have now worked on and tested two working solutions for how to get hold on the filtered data. Maybe thisos what you want, or at least can hint you where to go, so I thought I should share it =)
Option 1 - listen for changes in MaterialTable.state.data with reference. (useRef, and UseEffect)
Option 2 - built in MaterialTable.onSearchChange combined with reference to MaterialTable.state.data
note, I have included 2 flavors of option 2.
Thanks #tylercaceres for the example you provided, it didn't fit for me but gave me a hint on how to do it.
Code is found here: MaterialTableGettingHoldOfRenderData.js
material-table example getting filtered data, the tables current view data, including 2 options and some other examples of actions/buttons, how to use SvgIcon from Material-UI

Related

How to create a Crossword board in React Native

I'm trying to create a crossword game in react native. I'm having trouble starting off with the gameboard. I think I'm going to have the crosswords stored in an object like
{
across: {
1: {
question: "test",
answer: "test",
position:(0,0),
length: 4,
}
}
down:{}
}
Would it make sense to create a matrix of 0 for black squares 1 for white squares and 2 for word starting squares. Then use a flat list to build out the matrix visually?
Any help or advise on another way to do it would be appreciated.
Cheers,
I've. tried. using flat lists but the indexing becomes very complicated and I'm hoping there is a better way.
I made one of those React Pathfinding visualizers and basically just had an array I kept track of thru state for if it was filled or not. Map/ForEach that grid and plop down what you would have as another component shall we say Node passing whatever information is needed as props.
This example may not be the best, due to it being React and not React Native (small difference really)... and there is a lot to this that doesn't apply to your scenario but I think it shows what I mentioned in the beginning.
<div className="grid">
{grid.map((row, rowId) => {
return (
<div key={rowId}>
{row.map((node, nodeId) => {
const { row, col, isFinish, isStart, isWall } = node;
return (
<Node
key={nodeId}
row={row}
col={col}
isStart={isStart}
isFinish={isFinish}
isWall={isWall}
mouseIsPressed={this.state.mouseIsPressed}
onMouseDown={(row, col) => this.handleMouseDown(row, col)}
onMouseEnter={(row, col) =>
this.handleMouseEnter(row, col)
}
onMouseUp={() => this.handleMouseUp()}
></Node>
);
})}
</div>
);
})}
</div>
I'd map the crossword data to an array of fields. There are three types of fields in crosswords: question block, fillable block and dead block.
Algorithmically, there are countless options. One would be to first convert every question to its blocks and then convert all of these to a flat array of blocks, combined.
Extra tip: consider using an array of questions instead of an object indexed by numbers. These indexes don't matter anyway.

Multiple navigation.push same screen always go to the last of the list

I have a screen to display news articles. In a news there can be reference to other articles. On clicking on the reference i go to another news detail screen which display the new articles.
If have only one reference to another article, it works fine, even go back.
But if i have more reference, i always go to the last news article and not to the specific news article i am clicking on. The reference is the "id" of the article, pass via the param of a navigation.push, inside a .
Why is it always the last news id that is used ? I put the id (infNum) in the area, and it references the correct id of each article, but it seems that the param idNews is always the last infNum.
Here is my code :
'''
{news.results.texte.map((content, index) => {
if(content.substring(0,6) === '_Info_') {
{
finNum = content.indexOf("_T_");
finLien = content.indexOf("_/Info_");
infNum = content.substring(6,finNum);
txtLien = content.substring(finNum+3,finLien)
}
//console.log(finNum," ",finLien," ",infNum," ",txtLien)
return (
<TouchableOpacity
style={styles.txtLien}
key={infNum}
onPress={() => {
navigation.push("NewsDetail", {idNews:infNum})}
}
>
<Text>{txtLien}-{infNum}</Text>
</TouchableOpacity>
)
} else {
return (
<Text style={styles.description_text} key={index}>{content}</Text>
)
}
'''
I found the problem. Basic Javascript. The variable 'infNum' as it is not declared with the 'var' keyword is considered as global and so takes the last value given. I just put 'var infNum = content.substring ....' and it works well

Prepending data to a FlatList always shows the first child

this is our FlatList, say hello:
<FlatList
data={this.state.dates}
...
/>
we feed it with the following dates:
this.state = {
dates: [
'21/06/2019',
'22/06/2019',
'23/06/2019',
]
};
then when the visible date changes (onViewableItemsChanged), if we end up to the first item (21/06/2019), we prepend data, so that the new state becomes:
dates: [
'18/06/2019',
'19/06/2019',
'20/06/2019',
'21/06/2019',
'22/06/2019',
'23/06/2019',
]
The Problem:
right after we prepend the data, instead of STILL seeing 21/06/2019 (which was the date when the prepend took place) we now see 19/06/2019.
That's because below the hood, 21/06/2019 used to be index 0, but after the prepend, index 0 corresponds to 19/06/2019.
What we want:
I'm trying to make it so that the day remains the same after prepending data.
Please don't tell me to use scrollToPosition because that's a hack really, not the solution.
Is there any good solution to that problem?
Thank you
There is an undocumented prop maintainVisibleContentPosition on ScrollView that do what you want, but unfortunately it's not working on android
I found another workaround by keep latest y offset with onScroll and also save content height before and after adding new items with onContentSizeChange and calculate the difference of content height, and set new y offset to the latest y offset + content height difference!
I've opened an issue on github also, but there is not any complete solution yet
Thanks to sgtpepper43 for undocumented iOS solution
that maintainVisibleContentPosition does not work if you prepend data while you are at a scroll position of 0 (all the way to the top). It does work if you are prepending while not a the top. Even scroll Y of 1 would be enough.
check this
Unfortunately this still an issue even in React Native 0.63.3 and has not been solved.
Leaving this here, since it took me a while to get a working solution/ workaround to this problem that doesn't leave a bad taste in my mouth: prepopulate the array with empty data, and then use the scrollToIndex method.
this.state = {
dates: [
'',
'',
'',
'21/06/2019',
'22/06/2019',
'23/06/2019',
]
};
And then:
<FlatList
data={this.state.dates}
ref={flatListRef}
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
...
/>
inside your componentDidMount:
const startingIndex = 4
flatListRef.current.scrollToIndex({index: startingIndex, animated: false, viewPosition: 0})
There's a library: https://github.com/GetStream/react-native-bidirectional-infinite-scroll that allows you to both preprend or append data and preserve scroll position
It's basically an extension over the FlatList and supports all the props available for a plain FlatList
From their example usage tutorial: https://dev.to/vishalnarkhede/react-native-how-to-build-bidirectional-infinite-scroll-32ph#%F0%9F%96%A5-tutorial-chat-ui-with-bidirectional-infinite-scroll
It looks like it would be enough to just prepend that data to the top exactly like you're trying to do

Refer to a key in map to add content. React native

I am doing an app that gets information about a sports game from a provider. They provide goals and assists in two different objects, looks something like this:
incidents: {
1: {
id: 1,
type: 'goal'
},
2: {
id: 2
type: 'assist'
referto: 1
}
As you can see in the object above, the object with id 2 is an assist which refers to object with id 1.
So I want to map this object and return a <View> with the data, and if type = assist, I want it to append to the View which the id refers to.
Below is a mix of jQuery and React, but I hope you understand.
Object.map(incident => {
if (incident.type === 'assist') {
incident.referto.append( //refer to the View with key = incident.referto
<View><Text>I am an assist to the goal above</Text></View>
);
}
)};
How can I do something like this?
Thanks in advance!
EDIT:
I want to add "assist" view component inside the "goal" view component. I hope that will make it a bit clearer, sorry.
Best way of going at it is to use a conditional render.
{
incident.type === 'assist' &&
<View>
<Text>I am an assist to the goal above</Text>
</View>
}
By doing so you add the views only when incident type is assist, otherwise they are non-existent.
While not advised, another way of going at it would be to abuse React.createElement(component, props, ...children). However, such a solution is likely not what you want, and probably what you wish to achieve can be achieved using JSX.

Sectioning data in a ListView

Say you have a list of People incoming from your API.
[{content: 'John'},
{content: 'Tim'},
{content: 'Harry J. Epstein'}]
And you're looking to put people who are first-name-basis friends (John and Tim) under a section 'Friends' and people who are not (Harry J. Epstein) under 'Contacts'.
Tapping a friend selects them with a blue highlight, but tapping a 'contact' selects them with a red highlight.
Would the proper approach be to take the incoming data from the API, add a type: 'Friend', ... or type: 'Contact', ... around it, and section based on that type with separate a FriendItem and ContactItem class so I can split the highlighting function?
I've got a bunch of just basic ListView code that does this exact approach, but I'm basically looking for the easy way out, like Angulars ng-repeat equivalent.
So what's the React Native version of
var friends = api.getFriends()
var contacts = api.getContacts()
<div ng-repeat="friend in friends" ng-click="highlightFriend()"> ... </div>
<div ng-repeat="contact in contacts" ng-click="highlightContact()"> ... </div>
I'm struggling to understand how to split it. Do I need a FriendsPage, FriendsItem, and ContactsItem? Or put everything into one array in FriendsPage and use a FriendsItem that checks if it's a friend or contact and adds a function separately?
I feel like I'm slightly lost coming from MVC. I've got Redux running too, if there's an easy way using that.
Here is a nice example on how you can create section-dependent rows: https://github.com/spoeck/ListViewExample
The idea is basically to create the data blob properly, which is a bit tricky, and then in your renderRow callback, check the sectionID parameter:
_renderRow(rowData: any, sectionID: any, rowID: number) {
if (sectionID === this.data[0].section) {
return <MyFriends />
} else if (sectionID === this.data[1].section) {
return <MyContacts />
}else{
// ...
}
}
why don't you try SectionList
Use the new FlatList or SectionList component instead. Besides
simplifying the API, the new list components also have significant
performance enhancements, the main one being nearly constant memory
usage for any number of rows.