I am trying to pass the data from one screen to another and I have data in
this.props.data which has something like this ["12121","434536"], I am trying to do the following
trying to add that data to list via this.ds.cloneWithRows([values]),
but I am getting output like below:
["12121","434536"] in one row, its not adding the elements one after another.
let values= [];
values =this.props.data;
alert(values)
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: this.ds.cloneWithRows([values]),
<ListView
style={{width: '100%'}}
renderHeader={() => <Text style={styles.text}>adding Started</Text>}
dataSource={this.state.dataSource}
renderRow={(rowData) => <View style={{borderWidth:1, borderColor: '#232C46',borderRadius: 4}}>
<Text style={[styles.text, {backgroundColor: '#192034'}]}>{rowData}</Text>
</View>
}
/>
Agreed, with the first answer, it's a syntax error ("values" is already in the correct form, no need to put it in an array).
Also, just FYI, ListView is a deprecated component and you may want to convert this into a FlatList for fewer bugs, better performance / memory usage, and I think a more intuitive API.
https://facebook.github.io/react-native/docs/flatlist
Your data is already an array. You don't need the extra []
Change it like below;
this.ds.cloneWithRows(values)
Related
I'm stuck at a problem - I'm building a receipes app with Firebase Realtime. I've a working prototype, but I'm stuck with an issue where useEffect won't trigger a reload after editing the array [presentIngredients].
This how my presentIngredients is defined (note that presentIngredient is used to store the current user input before the user adds the ingredient. After that, the presentIngredient get's added to the presentIngredients!):
const [ presentIngredients, setPresentIngredients ] = useState([]);
const [ presentIngredient, setPresentIngredient ] = useState('');
My useEffect hook looks like that:
useEffect(() => {
console.log('useEffect called!')
return onValue(ref(db, databasePath), querySnapshot => {
let data = querySnapshot.val() || {};
let receipeItems = {...data};
setReceipes(receipeItems);
setPresentIngredients(presentIngredients);
})
}, [])
Here's my code to render the UI for adding/removing existing ingredients:
{ /* adding a to-do list for the ingredients */ }
<View style={styles.ingredientsWrapper}>
{presentIngredients.length > 0 ? (
presentIngredients.map((key, value) => (
<View style={styles.addIngredient} key={value}>
<Text>{key} + {value}</Text>
<TouchableOpacity onPress={() => removeIngredient(key)}>
<Feather name="x" size={24} color="black" />
</TouchableOpacity>
</View>
))
) : (
<Text style={styles.text}>No ingredients, add your first one.</Text>
)}
<View style={styles.addIngredientWrapper}>
<TextInput
placeholder='Add ingredients...'
value={presentIngredient}
style={styles.text}
onChangeText={text => {setPresentIngredient(text)}}
onSubmitEditing={addIngredient} />
<TouchableOpacity onPress={() => addIngredient()}>
<Feather name="plus" size={20} color="black" />
</TouchableOpacity>
</View>
</View>
And this is my function to delete the selected entry from my presentIngredients array or add one:
// update the ingredients array after each input
function addIngredient() {
Keyboard.dismiss();
setPresentIngredients(presentIngredients => [...presentIngredients, presentIngredient]);
setPresentIngredient('');
}
// remove items by their key
function removeIngredient(id) {
Keyboard.dismiss();
// splice (remove) the 1st element after the id
presentIngredients.splice(id, 1);
setPresentIngredients(presentIngredients);
}
The useEffect hook isn't triggered when adding an ingredient, however the change is instantly rendered on the screen. If I delete an item, the change isn't noticeable until I reload the screen - what am I doing wrong?
Note that all this is happening before data is send to Firebase.
Issues
There are a few overt issues I see with the code:
The code uses the array index as the React key, so if you mutate the array, i.e. add, remove, reorder, etc... the index values won't be the same as they were on a previous render cycle per array element. In other words, the React key is the same regardless what value is now at any given index and React likely bails on rerendering.
The removeIngredient callback handler is mutating the existing state instead of creating a new array reference.
Solution
Using the array index as a React key is bad if you are actively mutating the array. You want to use React keys that are intrinsically related to the data so it's "sticky" and remains with the data, not the position in the array being mapped. GUIDs and other object properties that provide sufficient uniqueness within the data set are great candidates.
presentIngredients.map((el, index) => (
<View style={styles.addIngredient} key={el.id}>
<Text>{el.key} + {index}</Text>
<TouchableOpacity onPress={() => removeIngredient(el.id)}>
<Feather name="x" size={24} color="black" />
</TouchableOpacity>
</View>
));
Use Array.prototype.filter to remove an element from an array and return a new array reference.
function removeIngredient(id) {
Keyboard.dismiss();
setPresentIngredients(presentIngredients => presentIngredients.filter(
el => el.id !== id
));
}
Note above that I'm assuming the presentIngredients data element objects have a GUID named id.
Listening for state updates
The useEffect hook isn't triggered when adding an ingredient, however
the change is instantly rendered on the screen.
The single useEffect only exists to run once when the component mounts to fetch the data and populate the local state. If you want to then issue side-effects when the state updates later you'll need a second useEffect hook with a dependency on the state to issue the side-effect.
Example:
useEffect(() => {
console.log("presentIngredients updated", { presentIngredients });
// handle side-effect like updating the fire store
}, [presentIngredients]);
you need to create another useEffect, because the first useEffect need to be called only once (because you are setting a watcher).
useEffect(() => {
// this effect will be executed when presentIngredients change
}, [presentIngredients]);
if you don't remember how the array of dependencies work
you can check the explanation here
https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
hopefully we are all in good health wherever we are.
I have a problem when I want to create a Quiz with Drag and Drop type in React Native. I'm using the react-native-drax library. The data I use comes from firebase, if it is described as follows :
[{
quest: "Book ?",
options: ["Yes","No"],
correct_option: "Yes",
type:'yesorno',
image:'urlofimages'
},
{
quest: "Fill the following words \n F_ _t Train M_ _ ern",
options: ["as","od","am"],
correct_option: ["as","od"],
type:'drag',
image:''
}]
(all quiz data is accommodated in the variable allData).
then I made the following components, to create a drag and drop area. I made it dynamic. Dropzone corresponds to the number of correct_option and TouchDrag corresponds to the number of options (taken from data).
import { DraxProvider, DraxView, DraxList } from 'react-native-drax';
....
.....
.......
const Exam = () => {
const [showAnwOne, setAnwOne] = useState([]);
const [showAnwTwo, setAnwTwo] = useState([]);
...
.....
.......
const Draxzone= ({item,index}) =>{
return(
<View style={{flexDirection:'row', justifyContent:'space-around', width:'100%', marginBottom:20}}>
<DraxView
style={styles.receiver}
key={item}
receivingStyle={styles.receiving}
onReceiveDragDrop={(event) => {
let selected_item = event.dragged.payload;
setAnwOne(selected_item)
}}
>
<Text>{showAnwOne}</Text>
</DraxView>
</View>
)
}
const TouchDrag = ({item,index})=>{
return(
<DraxView
style={styles.draggable}
draggingStyle={styles.dragging}
dragReleasedStyle={styles.dragging}
hoverDraggingStyle={styles.hoverDragging}
dragPayload={index}
longPressDelay={0}
key={item}
>
<Text style={{color:'#FFF'}}>{item}</Text>
</DraxView>
)
}
after that I display the component as follows:
(quiz is displayed based on its type)
{allData[currentIndex]?.type === 'drag' ?
<DraxProvider>
<View style={{width:'100%'}}>
<View style={{flexDirection:'row', justifyContent:'space-around', width:'100%', marginBottom:20}}>
{allData[currentIndex]?.correct_option.map((item, index) => Draxzone({ item, index }))}
</View>
</View>
<View style={{flexDirection:'row', justifyContent:'space-around', width:'100%', marginBottom:20}}>
<DraxList
data={allData[currentIndex]?.options}
renderItemContent={TouchDrag}
keyExtractor={(item, index) => item.toString()}
numColumns={3}
ItemSeparatorComponent={FlatListItemSeparator}
scrollEnabled={true}
/>
</View>
</DraxProvider>
: <Text>Sorry This Qustion not have type</Text> }
the result is executed as follows:
I tried to drag and drop components, the results are like in steps 2 and 3
while the result I want is as follows: Text on Dropzone follows text on TouchDrag
after that, I'm also confused to validate the correct answer in the order in correct_option. not like a quiz that only uses options from A to E for example. Honestly this is new to me, and I've been looking for quiz reference resources that use drag and drop in React Native, but I haven't found any results.
if there is something easier to understand, I ask for guidance. I hope that some of my friends here can help with this problem, thank you for your attention and time.
Not sure if this will fix your issue, but here are some suggestions:
Try to use unique IDs instead of indexes.
DraxView has a payload property in which you could pass in the ID and use it accordingly.
Good luck!
I wanted to know if KeyExtrator contributes to maintain the unique list ?
if yes then its not helping me at all.
I am trying to display elements on flat list when a new Item comes in, the list just addes it without checking for uniqueness of the list
Main goal is to make my list unique, I had tried ListView It was working
due to logic implemented using rowHasChanged method.trying to see if there is anything for FlatList.
constructor(props) {
super(props);
this.state = {
TESTDATA: [{
BarCode: '1',
StopAddress: 'Label 1'
}]
};
};
onReceivedMessage(messages) {
var jsondata = JSON.parse(messages)
var dataTest = this.state.TESTDATA;
var Data = addressToDataMap.get(dataTest.BarCodes);
dataTest.push(jsondata);
this.setState({
TESTDATA: dataTest
});
}
<FlatList
extraData={this.state}
data={this.state.TESTDATA}
keyExtractor={(item, index) => item.BarCode.toString()}
renderItem={({ item}) => (
<Text style={styles.baseText}>
Stop Address: {item.StopAddress}{'\n'}
Barcodes: {item.BarCode}
</Text>
)}
/>
I wanted to know if KeyExtrator contributes to maintain the unique list ? NO is the Answer
Explanation : keyExtractor tells the list to use the ids for the react keys instead of the default key property. Keys help React identify which items have changed, are added, or are removed. Key also used for caching and as the react key to tracking item re-ordering. These unique keys are what allow the VirtualizedList (which is what FlatList is built on) to track items and are really important in terms of efficiency. The keyExtractor falls back to using the index by default. For example
<FlatList
style={{}}
data={this.state.FeedDataCollection}
keyExtractor={(item, index) => item.key}
renderItem={(rowData) =>this.RenderFeedCard(rowData)}
/>
Here item.key is is key.So if you want display elements based on the uniqueness, you have write a logic for that. keyExtractor cant do that. Learn more about React Keys here https://reactjs.org/docs/lists-and-keys.html and KeyExtractor here https://facebook.github.io/react-native/docs/flatlist#keyextractor
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/>
I am building a small ReactNative + Redux app using a ListView. I was following the example code from the docs and came up with the following setup. My current implementation is very close to the sample code, but for some reason, I am getting an error when I want to "click" a list item.
Here are the relevant parts from my code:
JobsRootComponent.js
class JobsRootComponent extends Component {
...
_pressRow(row) {
console.log('JobsRootComponent - _pressRow ', row)
}
...
_renderRow(rowData, section, row) {
const title = rowData.title
const subtitle = 'by ' + rowData.by
return (
<TouchableHighlight onPress={() => this._pressRow(row)}>
<View style={styles.cellContainer}>
<Text style={styles.cellTitle}>{title}</Text>
<Text style={styles.cellSubtitle}>{subtitle}</Text>
</View>
</TouchableHighlight>
)
}
...
render() {
return (
<ListView
style={styles.container}
dataSource={this.props.dataSource}
renderRow={this._renderRow}
/>
)
}
...
}
This code looks fine to me, but for some reason, when clicking a list item, javacript crashes and displays the following two errors in the chrome dev console:
Cannot read property _pressRow of null
Unhandled JS Exception: Cannot read property _pressRow of null
According to my understanding, this means that the object whose _pressRow property was targeted with the click is actually null. But shouldn't that object be my JobsRootComponent? How come it can be null at this point?
After searching for a while, I came across this tutorial describing how to implement a simple ToDo-app which helped me solve the issue myself.
The problem was due to the way I assigned _renderRow to the renderRow-property of the ListView: Instead of simply doing renderRow={this._renderRow}, I needed to use javascript's bind() function:renderRow={this._renderRow.bind(this).
For reference, here is what my render() method now looks like:
render() {
return (
<ListView
style={styles.container}
dataSource={this.props.dataSource}
renderRow={this._renderRow.bind(this)}
/>
)
}