I'm currently trying to highlight the top element in a react native ListView.
Is it possible to for ListView identify which row component is located at the top of the view on scroll?
If not, how would I go about selecting the lowest integer rowID of the visible rows?
renderRow(rowData, sectionID, rowID) {
return (
<MyRow
{...rowData}
key={rowData.detailID}
onDetailPress={() => this.onDetailPress(rowData, rowID)}
/>
);
}
You can use onChangeVisibleRows which gives you a list of the visible rows and the rows that have changed their visibility. Something like this should do:
<MyRow
{...rowData}
key={rowData.detailID}
onDetailPress={() => this.onDetailPress(rowData, rowID)}
onChangeVisibleRows={(visible, changed) => this.highlightRow(visible[0])}
/>
You would probably need to update the data source with the item you want to be highlighted containing a state that will indicate it.
Related
I have a question. I have these code snippets implemented in my class component and while it is working for the most part, I need to actual display the most recent rendered component on top of the list instead of appended to the bottom of the list. How can I do that easily?
Code snippets:
state = {
formCount: 0
}
//click event for button
onAddClicked = () => {
this.setState({formCount: this.state.formCount + 1});
}
//render components based on count
{ [...Array(this.state.formCount)].map((_, i) => <Form key={i}/>)}
So I have form count in state and on clicking on a button, it will increment the count and then depending on the count, it will render the same component whenever I click on the button.
However, right now it is adding the new components to the bottom instead of the top, how do I render the new component that gets rendered when clicking the button to the top of the list instead?
You could use .reverse() along with .keys():
[...Array(this.state.formCount).keys()].reverse().map((i) => <Form key={i}/>)
So you want to render the components in reverse order? With the most "recent" ones at the top? A simple way would be to .reverse() the array of components
Material UI's DataGrid's API shows how to add a sort icon when the direction is ascending and another one when the sort direction is descending. These default to the arrow up and arrow down icons and can easily be changed through props. My design requires to also have an icon when the sorting direction is null (i.e., the user hasn't clicked on that column header yet but the up-and-down arrows indicate that the column is sortable).
I have tried a couple of solutions but I'm not satisfied with any of them. I tried using renderHeader inside the ColDef to implement the icons when SortModel changes but it doesn't look good enough, plus the DataGrid layout gets messed up for some reason when SortModel is changed.
Is there a better way to simply add a sortable icon next to the header titles when no sorting direction is defined?
Material-UI DataGrid does not have an option to set the icon in no-sort state. To do that, you need to get your hands dirty.
I tried using renderHeader inside the ColDef to implement the icons when SortModel changes but it doesn't look good enough
In order to replicate the built-in sort icon, you need to know which components get rendered in the column header when in sorting mode. Having a look at the DataGrid source code, you can see that:
In GridColumnHeaderItem: If you don't provide renderHeader, it will render GridColumnHeaderTitle instead. So we need to use that component to render the header.
In GridColumnHeaderSortIcon: This component is used to render the sort icon, the icon is wrapped inside an IconButton with a small size that itself is wrapped inside a container. Make sure to copy that code when you render your own header icon.
Putting it together, we'll have something like below:
import { GridColumnHeaderTitle } from "#material-ui/x-grid";
{
field: "firstName",
headerName: "First name",
width: 130,
renderHeader: (params) => {
const { field, api, colDef } = params;
const sort = api.state.sorting.sortModel.filter(
(s) => s.field === field
)?.[0]?.sort;
const isSorting = Boolean(sort);
return (
<>
<GridColumnHeaderTitle
label={colDef.headerName || colDef.field}
description={colDef.description}
columnWidth={colDef.width}
/>
{!isSorting && (
<div className="MuiDataGrid-iconButtonContainer">
<IconButton size="small">
<ClearIcon className="MuiDataGrid-sortIcon" />
</IconButton>
</div>
)}
</>
);
}
},
Live Demo
I have built a react application which shows items from JSON file, it consists of 4 main components, Items.js which created the item itself alongside its props (title and image), Itemlist.js which shows items from items.js and other functions.
MenuCat maps and shows each category from the json, and whenever the category is clicked it returns a number(props) to Itemlist.js adding it to the mapping function of items as ( children[{props here}].children[]....) so that it maps all items of each clicked category by using its json index.
Whenever an item is clicked, it shows more JSON properties such as promos inside a pop up called drawer, the content of this pop up is from Modifiers.js.
I need a function similar to MenuCat.js's category clicks but with items instead, where it shows the exact children of the item (e.g Burger 1 shows desert promo 1 on click, and burger 2 shows desert promo 2). The way its set up now, it only shows desert promo 1 for all the items.
Sandbox (current view on app.js): https://codesandbox.io/embed/8j5mmrjk2?fontsize=14&moduleview=1
One problem I noticed with your components was that you were passing the key attribute. This is anti-pattern as React does not pass down ref or key props.
If you need to access the same value within the child component, you should pass it as a different prop (ex: <ListItemWrapper key={result.id} id={result.id} />).
Just pass it down as a different attribute such as myKey.
I've done some other tweaks which you can check in the forked sandbox. Most notably, I added a state value to track the child index that was clicked.
this.state = {
itemSelected: 0
}
...
{items.children[selected].children[itemSelected].children.map(
(item, index) => (
<Modifiers
key={index}
title={item.name}
myKey={index}
childp={item.children[index].name}
>
{" "}
</Modifiers>
)
)}
Codesandbox link.
Okay, so the high level functionality of what I want this app to do:
Upon load, you're presented with the characters on the left, and on the right an intro screen.
When a user clicks on the first character (Cloud) for example, then the intro screen is replaced with Cloud's stats. If a user clicks on the second, third, etc character, same thing. The intro screen on the right is replaced with that character's specific stats.
That's basically it. I'm finding it really tough to do, for some reason.
For the /components/ folder breakdown (I'll go over the important ones):
/components/content/ - this is where the characters information lives, housed in the State. Each character is uniquely identified by an 'id'.
/components/sidebar/ - this is the sidebar component
/components/card-overview/ - this is a single component, repeated a few times, that houses the 'overview' of each character (image, character name), that you see in the sidebar itself
/components/card-detailed/ - this is where it grabs the array of objects from /components/content/, filters through and maps the character you're currently seeing to the DOM. Problem is, on line 13 of this component, you'll see I've hard-coded in the id #. Obviously, I want this to change to whatever character the user clicks, it then loads THAT character's info to the DOM. I just don't know how to do that.
In short, what I'm trying to do:
When a character on the left is clicked, hide whatever is on the right and replace it with that character's information.
I made your intention working here: https://codesandbox.io/s/l2lqm76z0q
As Chris pointed out, the way to go is using react state.
Therefore, I introduced a state with the default selected item 4 and created a handleClick function that should fire once you click on one item in your sidebar. Both of them got created in App.js:
state = {
selected: 4
};
handleClick = id => {
this.setState({ selected: id });
};
Now, I transferred the handleClick function where it is needed, in this case in Sidebar.js:
<Sidebar onClick={this.handleClick} />
And the currently selected item needs to be transferred to your content (again, where it is needed).
<Content selected={this.state.selected} />
In the sidebar, I send the handleClick function again to the it's child, CardOverview, with the related id(*):
<CardOverview
image={cloudImage}
name="Cloud Strife"
onClick={() => this.props.onClick(1)}
/>
And in CardOverview, finally, we use that function we passed down the road:
<section className="card-overview" onClick={this.props.onClick}>
...
</section>
(Whether it is working or not, we see in the console - when clicking on on item, it should show the state with the currently selected item id.)
Now, in content.js, we use the selected value and pass it down to CardDetailed where we use it in the following way:
// Grab the 'characters' object from App.js, and assign it to 'this.props'
const { characters, selected } = this.props;
// Filter the chracters and return only whose 'id' belongs to that of the selected one
const filteredCharacters = characters
.filter(character => character.id === selected)
.map(character => (
<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>
));
...
That`s it.
(*) Be aware, I did not improve your code, I just made it working. I see several issues that you should tackle. E.g. it is not needed to list all the characters in your sidebar manually. I'd suggest to create a file that contains your list of characters and use it in sidebar and in content. This way, you will reduce your code and maintain your characters list (and all belonging information like id) in one file.
Goal
I'm currently attempting to use React-Transition-Group to animate the rows of a table in and out. There are two different scenarios that can happen:
the entire contents of the table will be swapped out (most common option)
individual rows may be added and removed
Where I am now
I used this todo list example as a starting point to make the table animation. Here is my sandbox.
My main problem is when the table data is being completely swapped out, you can see both data sets at the same time. The old data is sliding out while the new data is sliding in. This causes the old data to snap down as it's transitioning out (see image below).
Ideally the initial table data would completely disappear before the new data comes in, but instead the new data pops in while the old data is there.
Code
Table Prop that maps through rows
<table>
<TransitionGroup component="tbody">
{this.props.tables[this.props.currentTable].rows.map((row, i) => {
return (
<CSSTransition
timeout={500}
classNames="SlideIn"
key={`${row}-${i}`}
>
<tr>
{row.map((column, i) => {
return <td>{column}</td>;
})}
</tr>
</CSSTransition>
);
})}
</TransitionGroup>
</table>
Function to change table dataset:
changeTable = () => {
this.setState({
currentTable:
this.state.currentTable < this.state.tables.length - 1
? this.state.currentTable + 1
: 0
});
};