Why it is not working ? I get no error...
## MEMO START ##
function isEqual(prev: IMyOrders, next: IMyOrders) {
if(prev.name=== next.name) {
return true;
} else {
return false;
}
}
const Item = ({
name,
t
}: { name: string; t: any; }) => {
return (
<Pressable style={s.item}>
<Text style={s.product_name}>Ordered Date: { t('profile.logged.reviews.created_at', { date: order_date }) }</Text>
</Pressable>
)
};
const MEMO_ITEM = memo(Item, isEqual);
## MEMO END ##
const SettingsMyOrders = () => {
const { t, i18n } = useTranslation();
const renderItem: ListRenderItem<IMyOrders> = ({ item }) => (
<MEMO_ITEM
{...item}
t={t}
/>
);
return (
<FlashList
data={data}
keyExtractor={(item, i) => item.name.toString()}
renderItem={renderItem}
estimatedItemSize={280.7}
/>
)
}
Why it is not working when I put this with memo ? when leave the memo and dont put it as prop then works but thats not the way I want
Try to write your custom are props equal function, because you have props as object. You can find more here -> https://dmitripavlutin.com/use-react-memo-wisely/
Related
This is my code:
export default function App() {
const [onProcess, setOnProcess] = useState("normal")
var myid = "123"
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://api.npoint.io/0294bea2185268c9ac70')
.then((response) => response.json())
.then((json) => setData(json))
.catch((error) => console.log('ERR :', error))
},[]);
for (let x in data) {
if (data[x].client_id == myid) {
var set = data[x].situation
setOnProcess(set)
console.log(data[x].situation)
break
}
}
const rt_normal = (
<View style={styles.container}>
<Text> This is normal view </Text>
</View>
)
const rt_process = (
<View style={styles.container}>
<Text> This is process view </Text>
</View>
)
if (onProcess == "normal") {
return rt_normal
}
else if (onProcess == "_on_process") {
return rt_process
}
}
The error I got is:
:[Render Error. Too many re-renders. React limits the number of renders to prevent an infinite loop.]
This happens because of setOnProcess(set) code. How can I solve this?
You should remove your for...in loop and refactor to utilise useEffect.
useEffect(() => {
// Get a specific entry where client_id matches myId.
const filteredItem = data.find(item => item.client_id === myId);
// Perform a check as .find() can return undefined.
if(filteredItem.situation) {
setSituation(filteredItem.situation);
}
}, [data]);
Put the for loop inside a useEffect
(Untested) example:
useEffect(() => {
for (let x in data) {
if (data[x].client_id == myid) {
var set = data[x].situation;
setOnProcess(set);
console.log(data[x].situation);
break;
}
}
}, [data]);
Sometghing really basic but I didn't understant.
Once I get the contacts how can I use them to populate the Flatlist?
I always get Can't find variable: contacts
import * as Contacts from "expo-contacts";
const ContactsScreen = props => {
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === "granted") {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.Emails]
});
if (data.length > 0) {
const contact = data[0];
console.log(contact);
}
}
})();
}, []);
return (
<View >
<Text>Contacts Module</Text>
<FlatList
data={contact}
keyExtractor={contact.id}
renderItem={({ item }) => (
<ContactItem
firstName={item.firstName}
/>
</View>
);
};
export default ContactsScreen;
I think it's really simple, I just don't understand
You need to keep your contacts in the component's state. So every time you change your state, your component will render itself and you will see the updated data.
Change your code with the following. Don't forget to import useState.
import * as Contacts from "expo-contacts";
const ContactsScreen = props => {
const [myContacts, setMyContacts] = useState([]);
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === "granted") {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.Emails]
});
if (data.length > 0) {
setMyContacts(data);
}
}
})();
}, []);
return (
<View >
<Text>Contacts Module</Text>
<FlatList
data={myContacts}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<Text>{item.firstName}</Text>
)}
/>
</View>
);
};
export default ContactsScreen;
Answer from my comment:
I think that might be because of the scope of the variable , it could be that RN doenst know it exists because it only lives inside the function. I guess you could set up a State and then assign the values from contact to the state and in ur flatlist call data ={ this.state.contact}.
or by using hooks like you do :
if (data.length > 0) {
setContact(data);
}
and call it in flatlist:
data={myContact} // if named so in state declaration
I'm trying to display the time zone in another screen when an item is pressed in previous flatlist. My data is coming from autocomplete when I'm selecting it is displayed in flatlist.
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={autotime.length === 1 && comp(query, autotime[0].name) ? [] : autotime}
defaultValue={this.state.timeZone}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter Location"
renderItem={({ name, release_date }) => (
<TouchableOpacity onPress={() => this.setState({ query: name,timezoneArray:autotime[0].timezones })}>
<Text style={styles.itemText}>
{name}
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{autotime.length > 0 ? (
<FlatList
style={{flex:1}}
data={this.state.timezoneArray}
renderItem={({ item }) => <TimeZoneItem text={item} />}
/>
) : (
<Text style={styles.infoText}>Enter Location</Text>
)}
I want that when I press the items of flatlist it is displayed on another page.
Picture below shows what I have made:
My Data base helper class is:
export const SaveItem = (key, value) => {
AsyncStorage.setItem(key, value);
};
export const ReadItem = async (key) => {
try {
var result = await AsyncStorage.getItem(key);
return result;
} catch (e) {
return e;
}
};
export function MultiRead(key, onResponse, onFailure) {
try {
AsyncStorage.multiGet(key).then(
(values) => {
let responseMap = new Map();
values.map((result, i, data) => {
let key = data[i][0];
let value = data[i][1];
responseMap.set(key, value);
});
onResponse(responseMap)
});
} catch (error) {
onFailure(error);
}
};
export async function DeleteItem(key) {
try {
await AsyncStorage.removeItem(key);
return true;
}
catch (exception) {
return false;
}
}
and here i have added my code to save
handleTimezone = (text) => {
this.setState({ TimeZoneItem: text })
}
newData.TimeZoneItem = this.state.TimeZoneItem
this.setState({
TimeZoneItem: '',
})
ReadItem('timeData').then((result) => {
let temp = []
if (result != null) {
temp = JSON.parse(result)
} else {
temp = []
}
temp.push(newData)
SaveItem('timeData', JSON.stringify(temp))
console.log(`New Data: ${JSON.stringify(temp)}`)
}).catch((e) => {
})
}
<FlatList
style={{flex:1}}
data={this.state.timezoneArray}
renderItem={({ item }) => (
<TouchableOpacity>
<TimeZoneItem text={item} onPress={() => this.props.onPress()}
value={this.state.TimeZoneItem}
/>
</TouchableOpacity>)}
You'll need an array to saved all the saved items.
for example
state = {
saved: []
};
On press the time zone item, get the values from state, add the new item to the array and save the array to async storage using JSON.stringify()
onSave = item => {
const { saved } = this.state;
const newItems = [...saved, item];
this.setState({
saved: newItems
});
const items = JSON.stringify(newItems);
SaveItem("saved", items)
.then(res => {
console.warn("saved", res);
})
.catch(e => console.warn(e));
};
Then in your other screen get the items in using your ReadItem function like.
state = {
saved: []
};
componentDidMount = () => {
ReadItem("saved")
.then(res => {
if (res) {
const saved = JSON.parse(res);
this.setState({
saved: saved
});
}
})
.catch(e => console.warn(e));
};
Working Demo
I have a JSON with the following shape for ~50 _source entries:
{
"hits": [
{
"_source": {
"name": "henry",
"jobs": ["judge", "lawyer"]
}
},
{
"_source": {
"name": "henry",
"jobs": ["dev", "waitress"]
}
}
// ...
]
}
Thanks to the community's help, I extracted each jobs as below:
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], [])
console.log(result) // this is an array
I extracted each item from result to add a string (for example "welcome judge"):
for(i in result)
{
var message = 'welcome'+ result[i] //this is a string
}
So now, I want to use a flatlist to render my message:
constructor() {
super()
this.state = { dataSource:'' }
}
componentDidMount() {
fetch('uri')
.then(response => response.json())
.then(json => {
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], []) // this is an array
for(i in result)
{
var message = 'welcome'+ result[i] //this is a string
}
this.setState({ dataSource : messsage})
}
renderItem =({item}) => {
return(
<View>
<Text>item</Text>
</View>)
}
render() {
return (
<View>
<FlatList
data= {[this.state.dataSource]}
renderItem= {this.renderItem}
/>
</View>
);
}
I got only one message (and not my list) and the warning 'missing key for item'
You should have keyExtractor={(x, i) => i.toString()} in your flatlist.
<FlatList
data= {[this.state.dataSource]}
keyExtractor={(x, i) => i.toString()}
renderItem= {this.renderItem}
/>
Here is a FlatList keyExtractor definition.
You will only get a single message, because your input data is just a string (converted to an array in render() to fit specifications). You change your single string variable in every iteration and update with the last changed one. You do need to push every string into an array, before you continue to the next item in the Iterable.
constructor() {
super()
this.state = { dataSource: [] }
}
componentDidMount() {
fetch('uri')
.then(response => response.json())
.then(json => {
// Get all jobs in a single array
const results = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], []);
// Iterate over results, concatenate with 'welcome' and push into a new array
let messages = [];
for(i in result)
{
let message = 'welcome'+ result[i];
messages.push(message);
}
// Update state with the new array 'messages'
this.setState({ dataSource : messages })
}
renderItem = ({ item }) => {
return(
<View>
<Text>{item}</Text>
</View>
);
}
render() {
return (
<View>
<FlatList
data={this.state.dataSource}
keyExtractor={(x, i) => i.toString()}
renderItem= {this.renderItem}
/>
</View>
);
}
Because your data source contains single string. Here you are updating the message var on each iteration, so it will have just the last string of the result array with 'hello' prepended.
for(i in result)
{
var message = 'welcome'+ result[i]
}
You should do something like this
componentDidMount() {
fetch('uri')
.then(response => response.json())
.then(json => {
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], [])
let messages=[];
for(i in result)
{
messages.push('welcome '+ result[i]); //push each element in array
}
this.setState({ dataSource : messsages})
}
Use key extractor to remove missing key warning
render() {
return (
<View>
<FlatList
data= {[this.state.dataSource]}
renderItem= {this.renderItem}
keyExtractor={(item, index) => item + index}
/>
</View>
);
}
I am not able to re-render my FlatList component. I have tried everything that I can think of. I have tried updating a boolean value for extraData with this.setState, I have passed Immutable.Map to extraData. But nothing seems to work although I know my component has updated correctly and everything is in place when component re-render, but FlatList does not re-render although I have passed changed data to extraData. I just cant figure out what I am doing wrong.
Here is my component without imports and styles:
class DetailsScreen extends Component {
constructor(props) {
super(props);
const {
data: { list, letterIndexes, alphabet },
savedList
} = this.props.navigation.state.params;
this.state = {
alphabet,
letter: "A",
letterIndexes,
letterIndexesValues: Object.values(letterIndexes),
savedList: savedList || [],
list,
updated: false
};
}
componentWillMount() {
const { list, savedList, refreshFlatList, updated } = this.state;
this.setState({
list: this.updateListActiveState(list, savedList),
updated: !updated
});
}
static navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params.title}`
});
/**
* Adds active prop to each object in array.
* If they exists in savedList then we set active prop as true
*
* #param {Array<Object>} list - Names list
* #param {Array<Object>} savedList - Saved names list
* #returns {Array<Object>} list
*/
updateListActiveState(list, savedList) {
return list.map(item => {
item.active = savedList.some(s => item.name === s.name);
return item;
});
}
/**
* Updates list names state with new state
*/
onClick = savedList => {
const { list, updated } = this.state;
this.setState({
list: this.updateListActiveState(list, savedList),
updated: !updated
});
};
/**
* Renders FlatList single item
*/
renderItem = ({ item }) => {
return <NameItem key={item.id} item={item} onClick={this.onClick} />;
};
onLetterPress(letter) {
const { letterIndexes } = this.state;
this.setState({ letter });
try {
this.flatListRef.scrollToIndex({
animated: true,
index: letterIndexes[letter]
});
} catch (e) {}
}
getItemLayout = (data, index) => {
return { length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index };
};
handleScroll = event => {
const { letterIndexes, letterIndexesValues } = this.state;
const y = event.nativeEvent.contentOffset.y + ROW_HEIGHT;
const index = parseInt(y / ROW_HEIGHT);
let letter = "A";
let cnt = 0;
for (const key in letterIndexes) {
if (letterIndexes.hasOwnProperty(key)) {
const startIndex = letterIndexes[key];
const endIndex =
letterIndexesValues[cnt + 1] !== undefined
? letterIndexesValues[cnt + 1]
: startIndex;
if (startIndex <= index && index <= endIndex) {
letter = key;
break;
}
}
cnt += 1;
}
if (letter !== this.state.letter) {
this.setState({ letter });
}
};
render() {
const { list, letterIndexes, alphabet, savedList, updated } = this.state;
const {
alphabetContainer,
container,
letter,
letterActive,
letterLast
} = styles;
console.log(updated);
return (
<View className={container}>
<FlatList
data={list}
keyExtractor={item => item.id}
renderItem={this.renderItem}
getItemLayout={this.getItemLayout}
initialNumToRender={20}
onScrollEndDrag={this.handleScroll}
extraData={updated}
ref={ref => {
this.flatListRef = ref;
}}
/>
<ScrollView
style={alphabetContainer}
showsHorizontalScrollIndicator={false}
>
<View>
{alphabet.map((l, i) => {
const active = l === this.state.letter ? letterActive : "";
const last = i === alphabet.length - 1 ? letterLast : "";
return (
<Text
key={i}
style={[letter, active, last]}
onPress={() => this.onLetterPress(l)}
>
{l}
</Text>
);
})}
</View>
</ScrollView>
</View>
);
}
}