How to measure a rows height in react-virtualized list - javascript

I'm a little uncertain as how to achieve dynamic heights of a List using react-virtualized.
I have a component as follows:
import { List } from 'react-virtualized';
<List
height={400}
rowCount={_.size(messages)}
rowHeight={(index) => {
return 100; // This needs to measure the dom.
}}
rowRenderer={({ key, index, style }) => <Message style={style} {...messages[index]} />}}
width={300}
/>
I have looked at using CellMeasurer as per the docs which says it can be used with the List component but I have no idea how this example actually works...
I've also tried to work out how it has been achieved in the demo code but have also reached a dead end.
Can someone please assist me on how I would measure the DOM to get each items height dynamically.

Sorry you found the docs to be confusing. I will try to update them to be clearer. Hopefully this will help:
import { CellMeasurer, List } from 'react-virtualized';
function renderList (listProps) {
return (
<CellMeasurer
cellRenderer={
// CellMeasurer expects to work with a Grid
// But your rowRenderer was written for a List
// The only difference is the named parameter they
// So map the Grid params (eg rowIndex) to List params (eg index)
({ rowIndex, ...rest }) => listProps.cellRenderer({ index: rowIndex, ...rest })
}
columnCount={1}
rowCount={listProps.rowCount}
width={listProps.width}
>
{({ getRowHeight, setRef }) => (
<List
{...listProps}
ref={setRef}
rowHeight={getRowHeight}
/>
)}
</CellMeasurer>
)
}
There are also demos of this component here showing how it's used.

Related

Observer does not work for recursive items

I'm really stuck with the problem. I have a nested menu. After click on the first menu level second level items shows up. After click on one of the children nothing happens. Looks like the observer don't work for deep levels items. I don't know if the "late" type could be a problem. Everything is described in the code:
https://codesandbox.io/s/mobx-state-tree-recursive-deep-menu-p7eqj
Thanks in advance
The problem is that when you are using your recursive TreeItem component it is not actually observer. You wrapping it in observer only when exporting, but TreeItem inside TreeItem is regular component, not observer so it doesn't react to anything.
Basically you just need to move observer decorator:
// Use `observer` right here straight away
const TreeItem = observer((props) => {
const handleClick = () => {
props.item.active ? props.item.deactivate() : props.item.activate();
};
const ItemChildren = ({ children }) => {
return (
<ul>
{children.map((child) => (
<TreeItem key={child.id} item={child} />
))}
</ul>
);
};
return (
<React.Fragment>
<li onClick={handleClick}>{props.item.id}</li>
{props.item.active && (
<ItemChildren children={values(props.item.children)} />
)}
</React.Fragment>
);
});
// Remove it from here
export default TreeItem;
Codesandbox

React - Slow state setting for dynamic rendered semantic UI dropdown component

I am new to React and playing around with Semantic UI to improve my skills. However, I am having some trouble with setting initial state and laggy state updates on dropdown change. I'm using the Semantic UI table component with an integrated dropdown following an example from the Semantic Docs here. Essentially I want to render table rows dynamically based on the number of products from a JSON array with each row having its own dropdown element pre-filled with a color from the JSON array . On dropdown change I want the specific index to update its state for the dropdown.
class DropdownExampleSelection extends Component {
constructor(props) {
super(props);
this.state = {
currentValues: {}
};
}
handleDropdownChange = (index, e, { value }) => {
this.setState(({ currentValues }) => {
currentValues[index] = value;
return currentValues;
});
};
render() {
const { currentValues } = this.state;
return (
<Table celled padded>
<Table.Header>
<Table.Row>
<Table.HeaderCell singleLine>Product</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{entries.map((entry, index) => {
return (
<Table.Row key={entry._id}>
<Table.Cell>
<Dropdown
placeholder="Select Color"
selection
options={colorOptions}
value={currentValues[index]}
onChange={this.handleDropdownChange.bind(this, index)}
/>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
);
}
}
export default DropdownExampleSelection;
See full code on codesandbox.io
There are two issues I am having:
1. I am not sure the correct way to set the initial state for each dropdown which should be the "color" from each product pre-populated as a dropdown value. See "entrylist.json" file in codesandbox above for the array
{
"_id": 21,
"title": "Product 21",
"color": "red"
}
2. The version in the above code sandbox updates the state at at a particular index, but the UI lags substantially when doing so. There are a good amount of products, but I think this is an issue somewhere in my code because when I use the default dropdown component from Semantic (with no custom state setup) codesandbox example here it does not lag and has the same amount of entries. Appreciate any guidance on this!
If you still look for to improve the performance of this. I have some suggestions.
#depish already said, you have too many Dropdown components and each one is rerendered, even if only 1 dropdown is changed. From this point we come to a question how can we render only Dropdown whose value has been changed?
In order to do that, you need to separate code below
return (
<Table.Row key={entry._id}>
<Table.Cell>
<Dropdown
placeholder="Select Color"
selection
options={colorOptions}
value={currentValues[index]}
onChange={this.handleDropdownChange.bind(this, index)}
/>
</Table.Cell>
</Table.Row>
);
to an outside React.PureComponent, and pass all the parameters which it needs (value, handleDropdownChange). Since PureComponent is rerendered only when there is a different props coming from parent, or state change.
It should solve your issue with getting all 150 dropdowns rendered
Good luck
u can replace your constructor function with this one (let me know your progress)
constructor(props) {
super(props);
this.initalstate= entries.map((entry, index) => {
return entry.color
})
this.state = {
currentValues: {...this.initalstate}
};
}

preserving state of accordion and expanded table rows

Is there any way in Ant Design to capture the state of whether particular table rows are expanded, and apply that state if the page refreshes or if the component remounts (i.e. when navigating between different routes on a SPA?)
Also is there way to make the expansion of table rows to work like an accordion. I.e. only one row can be expanded at a time?
At this point I am just investigating Ant-D, reading the documentation and playing with small code snippets.
But I can find no documentation on how to do the above (i.e. preserve/restore the state of table/accordions), so I would not know what to try. I certainly could build out custom bits and pieces, but then that would defeat the purpose of having a nice framework.
Hoping for a more elegant solution.
Check the next example, props you should refer to are:
onExpandedRowsChange
onExpand
expandedRowKeys
import { Table, Typography, Select, Radio } from 'antd';
const expandedRowRender = record => (
<Typography.Text code>{record.key}</Typography.Text>
);
export default function App() {
const [expandedRow, setExpandedRow] = useState(0);
const [radioValue, setRadioValue] = useState('accordion');
const [currentRow, setCurrentRow] = useState([]);
return (
<FlexBox>
<Radio.Group
style={{ width: 200 }}
value={radioValue}
onChange={e => setRadioValue(e.target.value)}
>
<Radio value="accordion">Accordion</Radio>
<Radio value="rowState">rowState</Radio>
</Radio.Group>
{radioValue === 'accordion' ? (
<Table
dataSource={dataSource}
columns={columns}
onExpand={(isExpanded, record) =>
setExpandedRow(isExpanded ? record.key : undefined)
}
expandedRowRender={expandedRowRender}
expandedRowKeys={[expandedRow]}
/>
) : (
<>
<Typography.Title>{currentRow}</Typography.Title>
<Table
dataSource={dataSource}
columns={columns}
expandedRowRender={expandedRowRender}
onExpandedRowsChange={setCurrentRow}
/>
</>
)}
</FlexBox>
);
}

React-virtualized. Calling public methods of List element has no effect

I am trying to use from React-virtualized.
In the following component I am trying to call public methods. The problem is, these methods are called (I see them called while debugging, but they have no visible effect.
import React, {Component} from "react";
import {List, AutoSizer, CellMeasurer, CellMeasurerCache} from "react-virtualized";
class InfiniteScroller extends Component {
constructor(props) {
super(props);
this.cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 50
});
this.state = {
currentLineSt: 50,
currentLineEnd: 100,
}
}
renderRow = ({index, parent, key, style}) => {
let className = "code-line";
if (this.state.currentLineSt <= index && this.state.currentLineEnd >= index) {
className += " code-line-focus";
}
return (
<CellMeasurer
key={key}
cache={this.cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
<div style={style} className={className}>
<span className={"code-index"}><b>{index}: </b></span>
<span className={"code"} style={{whiteSpace: "pre-wrap"}}>{this.props.data[index]}</span>
</div>
</CellMeasurer>
)
};
componentDidUpdate() {
// these methods are called but do nothing visible
this.myInfiniteList.forceUpdateGrid();
this.myInfiniteList.scrollToRow(100);
}
componentDidMount() {
// these methods are called but do nothing visible
this.myInfiniteList.forceUpdateGrid();
this.myInfiniteList.scrollToRow(100);
}
render() {
return (
<AutoSizer>
{
({width, height}) => {
return <List
ref={(ref) => this.myInfiniteList = ref}
forceUpdateGrid
rowCount={this.props.data.length}
width={width}
height={height}
deferredMeasurementCache={this.cache}
rowHeight={this.cache.rowHeight}
rowRenderer={this.renderRow}
/>
}
}
</AutoSizer>
);
}
}
export default InfiniteScroller;
I need to call them since:
1) After data change, line size does not change
2) Need a way to scroll to line on click.
Any ideas why it doesn't work or how I could do this differently would be greatly appreciated.
You'll have to talk more Brian to get a real understanding, but it appears your list isn't fully initialized on componentDidMount.
Note in this sandbox (took yours and tweaked): https://codesandbox.io/s/lro6358jr9
The log of the element in componentDidMount has an array of 0 for children, whereas the log when I click to do the same thing later has 26 children (and works fine).
I've noticed a lot of weird first load issues in react-virtualized usages (like your list not loading initially). Keep in mind that if you're giving react-virtualized data you expect it to update on, make sure that data is changing a prop somewhere. Otherwise nothing inside will re-render.

react native 100+ items flatlist very slow performance

I have a list just simple text that rendering into flatlist on react native but I am experiencing very very slow performance which makes app unusable.
How can I solve this? My code is:
<FlatList
data={[{key: 'a'}, {key: 'b'} ... +400]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>
Here is my suggestions:
A. Avoid anonymous arrow function on renderItem props.
Move out the renderItem function to the outside of render function, so it won't recreate itself each time render function called.
B. Try add initialNumToRender prop on your FlatList
It will define how many items will be rendered for the first time, it could save some resources with lot of data.
C. Define the key prop on your Item Component
Simply it will avoid re-render on dynamically added/removed items with defined key on each item. Make sure it is unique, don't use index as the key! You can also using keyExtractor as an alternative.
D. Optional optimization
Try use getItemLayout to skip measurement of dynamic content. Also there is some prop called maxToRenderPerBatch, windowSize that you can use to limit how many items you will rendered. Refer to the official doc to VirtualizedList or FlatList.
E. Talk is Cheap, show me the code!
// render item function, outside from class's `render()`
const renderItem = ({ item }) => (<Text key={item.key}>{item.key}</Text>);
// we set the height of item is fixed
const getItemLayout = (data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
);
const items = [{ key: 'a' }, { key: 'b'}, ...+400];
function render () => (
<FlatList
data={items}
renderItem={renderItem}
getItemLayout={getItemLayout}
initialNumToRender={5}
maxToRenderPerBatch={10}
windowSize={10}
/>
);
Try out this listview https://github.com/Flipkart/ReactEssentials, it renders far fewer items than FlatList and then recycles them. Should be much faster.
npm install --save recyclerlistview
check this link
https://github.com/filipemerker/flatlist-performance-tips
Example
FlatList
containerContentStyle={styles.container}
data={countries}
renderItem={({ item }) => (
<View style={styles.results}>
<Results
{...this.props}
country={item}
handleUpdate={this.handleUpdate}
pendingCountry={pendingCountry}
/>
</View>
)}
keyExtractor={item => item.alpha2code}
ListHeaderComponent={() => this.renderHeader()}
// Performance settings
removeClippedSubviews={true} // Unmount components when outside of window
initialNumToRender={2} // Reduce initial render amount
maxToRenderPerBatch={1} // Reduce number in each render batch
updateCellsBatchingPeriod={100} // Increase time between renders
windowSize={7} // Reduce the window size
/>
One of the simple ways to optimize your flatlist is by using React.memo. In technical words, it basically does a shallow comparison of your data and check whether they needs to be re-rendered or not.
Make a file such as ListComponent.js and add the renderItem JSX to it, and and it to the renderItem.
// ListComponent.js
import React, { memo } from "react";
import { StyleSheet, Text, View } from "react-native";
const ListComponent = ({ item }) => {
return <View ></View>
};
export default memo(ListComponent);
Here is your FlatList
<FlatList
data={data}
removeClippedSubviews={true}
maxToRenderPerBatch={8}
windowSize={11}
initialNumToRender={8}
keyExtractor={keyExtractor}
renderItem={({ item }) => (
<ListComponent item={item} />
)}
/>
Another optimization would be to provide a key using keyExtractor prop. It's very important.
I used 'react-native-optimized-flatlist'
and my problem was solved, the only thing to be careful about is that when you use this package, it removes keyExtractor and extraData
You can use react-native-optimized-flatlist. It is the optimized version of Flatlist.
1) Add this package by :
npm i -S react-native-optimized-flatlist
OR
yarn add react-native-optimized-flatlist
2) Replace <FlatList/> by <OptimizedFlatlist/>

Categories