I'm trying to print EquipmentID inside the flatlist
And for some reason it does not show me its information.
I would like to understand what is wrong with my code.
The data (props) come from another screen .
in my example i show the flatlist screen and the props as it print.
const EquipmentContentCardOptions = props => {
const renderItem = ({ item }) => {
return <Text>{props.EquipmentID}</Text>;
};
return (
<FlatList
data={props}
renderItem={renderItem}
keyExtractor={item => item.EquipmentID}
/>
);
};
export default EquipmentContentCardOptions;
this is the props :
{
"EquipSetData":[
{
"EquipmentID":"567",
"EquipmentName":"aaa",
},
{
"EquipmentID":"123",
"EquipmentName":"rrr",
}
]
}
you are passing props directly to the FlatList and then trying to access the EquipSetData's data so what you need to do is
<FlatList
data={props.EquipSetData}
renderItem={renderItem}
keyExtractor={item => item.EquipmentID}
/>
Hope this would resolve your issue
I would suggest you console.log(props) as well as console.log(item) to see if they aren't null.If renderItem is meant to be a component, then, you have defined it inside another component which is wrong. Plus the name must start with a capital letter (if it is meant to be a component). You probably need to include the FlatList component to see how the props is passed to it.
Related
I'm making a FlatList rendering Alarm items. (react-native-simple-alarm)
the tsx file where my FlatList is
const [alarms, setAlarms] = useState<AlarmType[]>([]);
...
const renderItem: ListRenderItem<AlarmType> = ({item}) => {
return <ListItem props={item} />; // i have an error here on props
};
...
<FlatList
ref={flatListRef}
scrollEnabled={scrollEnabled}
data={alarms}
renderItem={renderItem}
keyExtractor={item => item.oid} //error here on keyExtractor
/>
the Alarm list items that FlatList renders
import {Alarm as AlarmType} from 'react-native-simple-alarm/dist/Types';
const ListItem = (props: AlarmType) => {
const deleteItem = async (oid: string | number) => {
try {
await deleteAlarmById(oid);
...
in ListItem, I also get errors saying that oid cannot be undefined.
Not sure it's because of this problem, but the time properties of alarm instances that i made in application get messed up, as I make another instances
How can I use Alarm type perfectly?? please help me
You are passing the props incorrectly.
When you do something like
<ListItem props={item} />
You are passing props that contain a property called props, so in ListItem, the prop would be something like {props: item}.
But you want the prop in ListItem to be the item object, so you need to do something like
<ListItem {...item} />
To understand better, you can take a look at the docs for ... (triple dots) and also read Components and Props.
This is more of a conceptual question than anything else.
I'm trying to draw a SVG line between two elements in my application. The way I'm currently doing this is by having a top level ref, inside of which I store a ref for each of the child elements indexed by an arbitrary key. I then draw the arrows, deduced from a 2 dimensional array of pairs of these keys, look up the ref, and use the positioning of those elements to create the SVG's coordinates.
The problem with this method is, besides initial render, every render update afterwards uses outdated ref data as the children get rendered (and therefore positioned) after the parent, inside which the SVG layer is contained.
I've already thought of the obvious answers, useEffects, setStates, etc., but there doesn't seem to be a good solution here that I can think of. A lot of the obvious answers don't necessarily work in this case since they cause render loops. Another solution that I initially thought of was breaking down the arrow rendering to within each of the children, but this had problems of its own that initially caused the transition to having all of the refs live at the parent level.
My current solution is to key the SVG Layer component to a state variable, and then change the value of this variable in a useEffect once the final item renders, but this solution feels messy and improper.
Not exact but here's some code of the issue
Parent Component:
export default ({ items }) => {
const [svgKey, setSvgKey] = useState(0);
const pairs = items.reduce((arr, item) => {
for (const subItem of item.subItems) {
arr.push([subItem, item]);
}
}, [])
const itemRefs = useRef({});
return (
<>
{items.map(item => <Row items={items} setSvgKey={setSvgKey} item={item} refs={refs} key={item.key} />}
<SVGLayer key={svgKey} pairs={pairs} refs={refs} />
</>
);
}
SVG Layer
export default ({ pairs, refs }) => (
<svg>
{pairs.map(([a, b], i) => (
<Arrow key={i} a={refs.current[a.key]} b={refs.current[b.key]} />
)}
</svg>
);
Arrow
export default ({ a, b }) => <path d={[math for line coords here]} />;
Row
export default ({ refs, item, items, setSvgKey }) => {
useEffect(() => {
if (item.key === items[items.length - 1].key) {
setSvgKey(key => ++key);
}
});
return (
<div ref={el => (refs.current[item.key] = el)} />
);
}
I'm using a react-navigation. More specifically, I have a materialTabNavigator nested inside of a drawerNavigator. Each tab is in itself a stackNavigator. I have a button in homeScreen, that navigates to makePost.js. There I take in information and store it to Async storage using a simple wrapper.
In Posts.js there's a FlatList displaying each post as a component. The data for the FlatList is initially set correctly after making a request from Async Storage. The problem is that this only happens when the app is first opened. I have tried many different approaches to solve this. The only way so far I've found is to continuously setState in ComponentDidUpdate() in Posts.js. Obviously this is problematic, because it re-renders constantly. I can set a flag to stop is from rendering, but then it will not re-render again.
Ultimately, what I'd like to happen is that when I hit the user is done entering their information and is ready to make a post, they hit the button in makePost.js, and the data in the FlatList of Posts.js is update.
I've tried to pass parameters using navigation, does not work, parameters get lost somewhere, probably because of the nested navigators.
I could really used some guidance on the proper way to accomplish this.
( Navigators; not sure why this is forcing to one line )
---drawer
--tabNav
-home
homeScreen.js
makePost.js
-posts
posts.js
-messages
--drawer1
--drawer2
//Posts.js
export default class Posts extends React.Component {
state = {
rows: [
{id: 0, text: "dog"},
],
}
componentDidMount() {
this.loadState();
}
loadState = () => {
var value = store.get('posts').then((res => {
if (res === null) {
res = [{id: 0, text: "default"}]
} else {
res = res
}
this.setState({rows: res})
}))
}
componentDidUpdate() {
this.loadState();
}
renderItem = ({item}) => {
return (
<BoardTab style={styles.row} />
)}
render() {
return (
<View style={styles.view}>
<FlatList
ListFooterComponent={this.renderFooter}
style={styles.container}
data={this.state.rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
>
</FlatList>
<BoardScreenFooter />
</View>
);
}
And Posts.js button looks like this:
<TouchableOpacity
onPress={ () => {
this._onPressButton
this.storeFunc(this.state.newPost)
const retval = this.state.rows
this.props.navigation.navigate('Board',
{rowsID: retval});
}
}>
<Icon
reverse
name='md-camera'
type='ionicon'
color='green'
size={12}
/>
</TouchableOpacity>
storeFunc(newObj) {
newObj.id = newObj.id + 1
store.push('posts', newObj)
store.get('posts').then((res) => {
this.setState({rows: res})
})
}
Rapidly, i would say: use Redux. It alloq you to have global state in your app, which mean you can access the state anywhere (And also set them anywhere)
When opening the app, you get the data from the AsyncStore into the Redux store. You listen to the redux state (Which will be a props in your component) and display your list. When modifying your list in the other tab, you need to do 2 things:
Store the new data in the AsyncStorage
Update the state in the redux store. Since Posts.js will be listening at the redux store (as a props), it will re-render each time your data will change
A simple way to re-render a React-Navigation screen view on navigating to it:
All credit goes to Andrei Pfeiffer, Jul 2018, in his article: "Handle Tab changes in React Navigation v2" https://itnext.io/handle-tab-changes-in-react-navigation-v2-faeadc2f2ffe
I will reiterate it here in case the above link goes dead.
Simply add a NavigationEvents component to your render function with the desired listener prop:
render() {
return (
<View style={styles.view}>
<NavigationEvents
onWillFocus={payload => {
console.log("will focus", payload);
this.loadState();
}}
/>
<FlatList
ListFooterComponent={this.renderFooter}
style={styles.container}
data={this.state.rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
>
</FlatList>
<PostScreenFooter />
</View>
);
}
https://codesandbox.io/s/qYEvQEl0
I try to render a list of draggables, everything seems fine only that I can't figure out how to pass 'index' into rowRenderer
If I do rowRenderer=props => <Row {...props}/>, index is passed in sucessfully.
But if I do:
const SortableRow = SortableElement(Row)
rowRenderer=props => <SortableRow {...props}/> ,
index is blocked somehow, failed to pass into <Row/>
Basically, I don't understand what can go wrong when you wrap your <Row/> component with a HOC? Why some props get to pass in, others not?
Copy the index into a different, custom prop...
rowRenderer = props => {
console.log(props.index);
return <SortableRow {...props} indexCopy={props.index} />;
};
Then, inside the child component, refer to this custom prop instead.
const Row = ({ indexCopy , style }) => {
console.log(indexCopy);
return (
<div style={style}>
<span>drag</span>
<input placeholder={'haha'} />
<span>index={indexCopy || 'undefined'}</span>
</div>
);
};
I'm not too familiar with HOCs, but I suspect that the react-sortable-hoc library is stripping out the implicit index and key values. However, as long as you copy them over into their own custom props, you should be fine.
I use FlatList with large number of items. I get following alert from Expo XDE.
VirtualizedList: You have a large list that is slow to update - make
sure your renderItem function renders components that follow React
performance best practices like PureComponent, shouldComponentUpdate,
etc. {"dt":13861,"prevDt":1498372326027,"contentLength":6624}
I used some optimization approaches to my FlatList for example PureComponent, but I still get this alert. Before I will describe my optimizations, could you tell me if this alert appears always even though FlatList is optimized? Or maybe it indicated actual issues with performance? I ask because performance of my FlatList is good.
I was previously seeing this error. After optimizing my code, I no longer see it. I figured out the problem by adding console.log() statement to the render() function of the Component that creates the FlatList, and the function that renders each item in the List. I noticed that my code was previously re-rendering the entire FlatList and all its items whenever there's a state change to any component on that page (even a component that's not related to the FlatList). I fixed this by converting various components to PureComponents. Here's what my FlatList declaration looks like:
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
data={allPosts}
initialNumToRender={7}
renderItem={({ item }) =>
<Post postJson={item} isGroupAdmin={isGroupAdmin} user={user} />
}
/>
Notice that I'm returning <Post /> which is a pure component:
import React, { PureComponent } from 'react';
class Post extends PureComponent {
render() { ... }
}
This ensures that the FlatList re-renders a only if the post changes. When I was previously passing a normal function to renderItem i.e., a function that does something like this:
return (
<View>
...
</View>
);
I noticed that the FlatList was re-rendering all items whenever any item changed. Now, by using a PureComponent, the FlatList only renders the new item added to the list (if the list is already being displayed).
It still takes relative long to render the entire list the first time. However, initialNumToRender ensures that the screen is filled up pretty much instantaneously (while the remain items get rendered in the background). And more importantly, after that initial rendering, the FlatList only ever has to render one item at a time (the item that changes).
I found this post very helpful).
I've just realized this is also explained here
I noticed that the answer to this question dosen't proffer solution for those using functional component and hooks. I encountered this problem and i was able to get rid of it by using the hook "useMemo()"
<FlatList
keyExtractor={keyExtractor}
data={productsState.products}
renderItem={renderItem}
/>
const renderItem = ({ item }) => (
<ListItem
title={item.ProductName}
subtitle={(item.ProductQuantity) + " " + (item.QuantityType !==
null ? item.QuantityType : " ") }
bottomDivider
topDivider
chevron
checkmark={checkMark}
onLongPress={() => setCheckMark(!checkMark)}
rightSubtitle={(item.Currency !== null ? item.Currency: " " ) +
" " + (item.productCost !== null ? item.productCost: " " )}
rightSubtitleStyle={{ marginTop: -20 }}
badge={{ value: item.sellingPrice, textStyle: { color: 'orange' }, containerStyle: { marginTop: -20 } }}
/>
)
The renderItem function is an expensive computation, because it a long list to render. Instead I memoize it as follows
const memoizedValue = useMemo(() => renderItem, [productsState.product]);
<FlatList
keyExtractor={keyExtractor}
data={productsState.products}
renderItem={memoizedValue}
/>
const renderItem = ({ item }) => (
<ListItem
title={item.ProductName}
subtitle={(item.ProductQuantity) + " " + (item.QuantityType !==
null ? item.QuantityType : " ") }
bottomDivider
topDivider
chevron
checkmark={checkMark}
onLongPress={() => setCheckMark(!checkMark)}
rightSubtitle={(item.Currency !== null ? item.Currency: " " ) +
" " + (item.productCost !== null ? item.productCost: " " )}
rightSubtitleStyle={{ marginTop: -20 }}
badge={{ value: item.sellingPrice, textStyle: { color: 'orange' }, containerStyle: { marginTop: -20 } }}
/>
)
Don't forget to import useMemo from react, inorder to make this work.
Good Luck!
If you are using a functional component, wrapping the component in memo is a good way to prevent unnecessary renders without going through the hassle of converting a functional component to a pure class component. This post explains it more
follow this example:
In the parent component:
import React from 'react';
import {FlatList} from 'react-native';
import PostCard from './PostCard';
export const NewsFeeds = props => {
return (
<FlatList
data={data}
initialNumToRender={4}
refreshing={loading}
renderItem={_renderitem}
/>
);
};
const _renderitem = ({item}) => <PostCard item={item} />;
In the child component
import React, {memo} from 'react';
import {View} from 'react-native';
const PostCard = (props) => {
return (
<View>
</View>
);
};
export default memo(PostCard);
If you are using a class component, make sure your component is a pure component by extending React. PureComponent in your class definition
class NewsFeeds extends React.PureComponent {
render() {
return (
<FlatList
data={data}
initialNumToRender={4}
refreshing={loading}
renderItem={_renderitem}
/>
)
}
}
Adding this prop :
initialNumToRender={n}
worked for me (n being a considerably short amount, for example 5).
I figured it out, why this bug is happened. The main problem is, when your onEndReached event is happened, im sure you are loading something from server, which means, you need to wait until your loading is finished from server, so after that you can call onEndReached event.
But in your case there is multilple calling of onEndReached event. So when it happens, your application was trying to load datas from server again and again.
Ok, how to solve this problem: you need to create new state, for example
this is realization of infinite scrolling by pagination.
const [loader, setLoader] = useState<boolean>(false);
const onEndReached = (page) => {
if (next && !loader) {
setPage(page + 1)
}
}
const loadData = async () => {
setLoader(true);
const resp = await getData();
setLoader(false);
}
<FlatList ...someprops onEndReached={onEndReached} />
On top of all the answers given, you can also try setting removeClippedSubviews to true.
<FlatList
removeClippedSubviews
// ...other props
/>
By enabling removeClippedSubviews the memory is freed up when an item disappears from the view. When you have a long and complex list (i.e. a list of cards) the DOM of each card can get pretty large so it's best to free up the memory when it's not visible.
In addition if you combine with useCallback() rather than useMemo() you free up a bit more memory when your "data" changes
const renderItem = useCallback(originalRenderItem, [data])
the useMemo() approach will memoize based on the value, but it should really free itself up when the data changes. By doing useCallback() you're getting the benefit of using the "function as a parameter" so you don't need to
const renderItem = useCallback(({item, index}
=> originalRenderItem({item, index}), [data])
Thus making it look like a wrapped function without as much reading for the next person.
Doing this two:
prevents the calling the potentially expensive render() function of recently updated components.
reduces memory used by invisible components
frees up the memoized data if data changes sooner.
Also make sure, you don't encapsulate FlatList with ScrollList.
For me it accidentally appears, because I used native-base, and didn't noticed, that their Component <Content> replace ScrollList.
For more Information see here: https://stackoverflow.com/a/54512633/1256697
add memo to your renderItem component when export it
import React,{memo} from "react";
.
.
.
your code
.
.
.
export default memo(your component name);