Edit A Single Item and save formers data (FlatList) - react-native

Hi I'm trying to make a "like" system as you can see on instagram -> when i press the heart it becomes red and full, and if I press it again it becomes again black and with outline. When I Press one heart other must stay with their actual style
, but I'm encountering this error. : initializeAnArray is not a function... Please can you help me.
If maybe you have a better way to code what i'am trying to do I don't refuse your tips. Thank You
const data = [
{
id: "1",
message: "Hi",
},
{
id: "2",
message: "Hello",
},
{
id: "3",
message: "Welcome",
},
];
function Heart(props) {
const [dataUser, setDataUser] = useState(data);
const [heart, setHeart] = useState(initializeAnArray("heart-outline"));
const [heartColor, setHeartColor] = useState(initializeAnArray("black"));
const [getIndex, setGetIndex] = useState(-1);
const initializeAnArray = (value) => {
let newArray = [];
for (let i = 0; i < dataUser.length; i++) {
newArray.push(value);
}
return newArray;
};
const editArray = (array, id, value) => {
array[id] = value;
return array;
};
const heartPress = (id) => {
setGetIndex(id);
if ((id = getIndex)) {
heartColor[id] === "red"
? setHeartColor(editArray(heartColor, id, "black")) +
setHeart(editArray(heart, id, "heart-outline"))
: setHeartColor(editArray(heartColor, id, "red")) +
setHeart(editArray(heart, id, "heart-outline"));
}
setHeartColor(editArray(heartColor, id, heartColor[id]));
setHeart(editArray(heart, id, heart[id]));
};
return (
<Screen>
<FlatList
data={dataUser}
extraData={dataUser}
renderItem={({ item }) => (
<View style={styles.container}>
<View style={styles.message}>
<Text>{item.message}</Text>
<TouchableOpacity onPress={heartPress(parseInt(item.id) - 1)}>
<MaterialCommunityIcons
name={heart[parseInt(item.id) - 1]}
size={12}
color={heartColor[parseInt(item.id) - 1]}
/>
</TouchableOpacity>
</View>
</View>
)}
/>
</Screen>
);
}
const styles = StyleSheet.create({
container: {
alignItems: "center",
},
message: {
flexDirection: "row",
marginBottom: 20,
},
});
export default Heart;

can you add keyExtractor={item => item.id} to your component FlatList and try again
something like this:
<FlatList
data={dataUser}
extraData={dataUser}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View style={styles.container}>
<View style={styles.message}>
<Text>{item.message}</Text>
<TouchableOpacity onPress={heartPress(parseInt(item.id) - 1)}>
<MaterialCommunityIcons
name={heart[parseInt(item.id) - 1]}
size={12}
color={heartColor[parseInt(item.id) - 1]}
/>
</TouchableOpacity>
</View>
</View>
)}
/>
https://reactnative.dev/docs/flatlist

Related

Understanding UI State : Dynamic TextInput loses focus after each key press

Screens and navigating is fine, but getting data from users has been a struggle. Not only are the UI elements different, the means to capture input and store in state effectively across various input types is troubling me. Here is an example of what I think is a simple UX yet I cannot get the text inputs to focus correctly.
In the below example, the desire is to have a list of items within a horizontal scroll view and when i click on the arrow of an item, the screen scrolls and a form to edit the item appears with one or more text boxes. Future enhancements were to have this second panel have more fields based on the type of field from the list, but i cant even get a simple text box to work properly
I've got some code to boot, copy and paste as app.js in an expo init project and it should run
main question: how to retain focus on inputs on the detail panel
import React from "react";
import {
Dimensions,
FlatList,
SafeAreaView,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
const init_items = [
{ name: "start", value: 12500, type: "num" },
{ name: "end", value: 12700, type: "num" },
{ name: "time", value: 123.45, type: "time" },
];
const main_color = "#dddddd";
const _base = 3;
const _width = Dimensions.get("window").width;
export default function App() {
const [index, setIndex] = React.useState(0);
const [items, setItems] = React.useState(init_items);
const [curItem, setCurItem] = React.useState("");
const ref = React.useRef(null);
const textRef = React.useRef(null);
React.useEffect(() => {
console.log("index chsnaged?", index);
//if (!index) return;
ref.current?.scrollToIndex({ index, animated: true });
}, [index]);
React.useEffect(() => {
setIndex(curItem === "" ? 0 : 1);
}, [curItem]);
const useCurItem = () => {
if (curItem == "") return;
return items.find((item) => item.name == curItem);
};
const setCurItemValue = (value) => {
console.log("update " + curItem + " to " + value);
const new_items = items.map((item) => {
if (item.name == curItem) return { ...item, value: value };
return item;
});
console.log("new_items: ", new_items);
setItems(new_items);
};
const Button = ({ type, press }) => {
return (
<TouchableOpacity onPress={() => press(type)}>
<Text style={{ fontSize: 20, fontWeight: "900", margin: _base }}>
{type == "arrow" ? ">" : "X"}
</Text>
</TouchableOpacity>
);
};
const ListPanel = () => {
return (
<View>
{items.map((item) => {
return (
<View
key={item.name}
style={{
margin: _base,
}}
>
<Text style={{ fontWeight: "600", margin: _base }}>
{item.name}
</Text>
<View
style={{
alignItems: "center",
backgroundColor: "white",
borderRadius: _base,
flexDirection: "row",
justifyContent: "space-between",
margin: _base,
padding: _base,
}}
>
<Text>{item.value}</Text>
<Button type="arrow" press={() => setCurItem(item.name)} />
{/* <EmojiButton
name={"fat_arrow"}
onPress={() => setCurItem(item.name)}
size={20}
/> */}
</View>
</View>
);
})}
</View>
);
};
const DetailPanel = () => {
let thisItem = useCurItem();
if (!thisItem) return null;
return (
<View style={{ width: "100%" }}>
{/* <EmojiButton name="arrow_left" onPress={() => setCurItem("")} /> */}
<Button type="cancel" press={() => setCurItem("")} />
<Text>{curItem}</Text>
<Text>{thisItem?.value}</Text>
<Text>{thisItem.type}</Text>
{thisItem.type == "num" && (
<TextInput
ref={textRef}
onChangeText={(text) => setCurItemValue(text)}
// onSubmitEditing={() => textRef.current.focus()}
style={{ backgroundColor: "white", margin: 2 }}
value={thisItem.value.toString()}
/>
)}
</View>
);
};
const screens = [
{ name: "listing", panel: <ListPanel /> },
{ name: "detail", panel: <DetailPanel /> },
];
return (
<View style={{ marginTop: 30 }}>
<Text>Sample sliding inputs</Text>
<FlatList
bounces={false}
horizontal
keyExtractor={(item) => item.name}
ref={ref}
showsHorizontalScrollIndicator={false}
data={screens}
renderItem={({ item, index: fIndex }) => {
console.log("rendering " + item);
return (
<View
style={{
backgroundColor: main_color,
height: 300,
width: _width,
padding: _base,
}}
>
<Text> {item.name}</Text>
{item.panel}
</View>
);
}}
/>
<Text>index: {index}</Text>
<Text>curItem: {curItem}</Text>
<TouchableOpacity onPress={() => setCurItem("")}>
<View>
<Text>reset</Text>
</View>
</TouchableOpacity>
</View>
);
}

Flat list single item with two buttons

A flat list displays information from an API, I want to add a delete button for each item. Where user can click on it and be able to delete this specific item.
Please check my code below.
<FlatList
numColumns={1}
data={this.state.allDocs}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => Linking.openURL('http://URL')}>
<Text>{item.docName}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={deleteFunction}>
<Text>Delete</Text>
</TouchableOpacity>
)}
keyExtractor={item => item.docName}
/>
import React, { Component } from "react";
import {
SafeAreaView,
View,
FlatList,
StyleSheet,
Text,
TouchableOpacity
} from "react-native";
export default class Example extends Component {
state = {
data: [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Item"
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Item"
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Item"
}
]
};
renderItem = ({ item }) => {
return (
<View style={styles.item}>
<Text>{item.title}</Text>
<TouchableOpacity onPress={() => this.removeValue(item.id)}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
);
};
removeValue = id => {
let newData = this.state.data.filter(item => item.id !== id);
this.setState({
data: newData
});
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
flexDirection: "row",
justifyContent: "space-between"
}
});
Change this according to your requirements.
Hope this will helps you. Feel free for doubts.
You just need to pass a function that will remove the document from your state like this:
export default class FlatListExample extends Component {
_openDoc = async (index) => {
const { allDocs } = this.state;
Linking.openURL(allDocs[i].url);
}
_deleteDoc = (index) => {
const { allDocs } = this.state;
allDocs.splice(index, 1);
this.setState({ allDocs });
}
render() {
const { allDocs } = this.state;
return (
<FlatList
data={allDocs}
keyExtractor={item => item.docName}
renderItem={({ item, index }) => (
<Fragment>
<TouchableOpacity onPress={() => this._openDoc(index)}>
<Text>{item.docName}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => this._deleteDoc(index)}>
<Text>Delete</Text>
</TouchableOpacity>
</Fragment>
)} />
);
}
}

react-native changes the properties of the elements in the array?

I have a FlatList and I want to implement a radio button.My idea is to change the selected property of an element in this.state.data to control it,but I am a newbie, I don't know how to change the property of an element in this.state.data.
Here is my code:
this.state = {
data: [
{
month:1,
price:18,
selected:true
},
{
month:3,
price:48,
selected:false
},
{
month:12,
price:128,
selected:false
},
],
};
<FlatList
data={this.state.data}
renderItem={({item, index, separators}) => (
<TouchableOpacity onPress={() => this.radio(index,item)}>
<View style={item.selected ? {borderWidth:3,borderColor:'#FFA371',borderRadius:15}:{}}>
<View style={styles.itemDefalut}>
<View style={{ flexDirection: "column", flex: 1 }}>
<Text>
Months
</Text>
<Text>{item.month} Months</Text>
</View>
<View>
<Text>${item.price}</Text>
</View>
</View>
</View>
</TouchableOpacity>
)}
/>
radio(index,item) {
for (var variable in this.state.data) {
variable.selected = false;
}
item.selected = true;
}
first pass only index from onpress
onPress={() => this.radio(index)
then in radio function do something like this
radio = index => {
let data = [ ...this.state.data ];
this.state.data.map((elem,key)=>{
if(elem.month==data[index].month){
data[key]={...data[key], selected: true};
}else{
data[key]={...data[key], selected: false};
}
})
this.setState({ data:data});
}
radio(item) {
let data = [...this.state.data];
let index = data.findIndex(el => el.month === item.month);
data[index] = {...data[index], selected: !item.selected};
this.setState({ data });
}
In TouchableOpacity on press it should be
<TouchableOpacity onPress = {this.radio.bind(this,item)}>

Passing values after clicking on the Button - React Native

How can I pass all values inside Render() method after I click on the button Send? Inside this Render() method I return some Flatlists, Views, Signature, etc. So, is it possible to pass all of these values to another page only by a click of a button.
Please let me know if you dont have the question clear so I can add some more explanation.
See below for the code (Edited).
I appreciate any suggestion or help!
EDIT:
renderTextandInputs = (obje) => {
var keyvalue_to_json = JSON.parse(obje.keyValues);
var foundTextFields = [];
for (let i = 0; i < keyvalue_to_json.length; i++) {
if (keyvalue_to_json[i].type === 'textfield') {
foundTextFields.push(<TextInput style={{ borderWidth: 1, flex: 1, alignItems: 'flex-start' }}>{keyvalue_to_json[i].placeholderText}</TextInput>)
}
}
return (
<View>
<ScrollView>
<ListItem
title={obje.name}
subtitle={obje.description}
/>
</ScrollView>
<View >
{foundTextFields}
</View>
</View>
)
}
render() {
const obj = this.props.navigation.state.params.item;
var propsArray = [];
const itemArray = Object.assign(obj)
propsArray.push(itemArray)
keyExtractor = (item, index) => {
return index.toString();
}
return (
<View>
<View>
<FlatList
key={this.keyExtractor}
data={propsArray}
renderItem={({ item }) => this.renderTextandInputs(item)}
/>
</View>
<View >
{this.state.signature ? (
<Image
resizeMode={"contain"}
source={{ uri: this.state.signature }}
/>
) : null}
</View>
<Modal isVisible={this.state.isModalVisible}
onBackdropPress={() => this.setState({ isModalVisible: false })}
>
<Signature
width="100"
onOK={this.handleSignature}
descriptionText="Sign"
clearText="Clear"
confirmText="Save"
webStyle={style}
/>
</Modal>
<View>
<Button title="SIGN" onPress={this._toggleModal} />
</View>
<View>
<Button title="Send" onPress={this._onSendDoc} />
</View>
</View>
);
}
_onSendDoc = (item) => {
this.props.navigation.navigate('Detail', { item: item })
}
}
if you check here: https://facebook.github.io/react-native/docs/flatlist you can render a button per flatlist item like this:
EDIT
_onSendAll = () => {
const obj = this.props.navigation.state.params.item;
var propsArray = [];
const itemArray = Object.assign(obj)
propsArray.push(itemArray)
this.props.navigation.navigate("Detail", { allData: propsArray });
};
_onSendDoc = item => {
this.props.navigation.navigate("Detail", { item: item });
};
render() {
return (
<FlatList
data={[{title: 'Title Text', key: 'item1'}]}
renderItem={({item}) => (
<TouchableHighlight
onPress={() => this._onSendDoc(item)}
<View style={{backgroundColor: 'white'}}>
<Text>{item.title}</Text>
</View>
</TouchableHighlight>
)}
/>
)
On each button clicked, the item data passed will be logged.

React Native FlatList won't render

I'm trying to implement a FlatList in my React Native application, but no matter what I try the darn thing just won't render!
It's intended to be a horizontal list of images that a user can swipe through, but at this point I'd be happy to just get a list of text going.
Here's the relevant code. I can confirm that every render function is called.
render() {
const cameraScreenContent = this.state.hasAllPermissions;
const view = this.state.showGallery ? this.renderGallery() : this.renderCamera();
return (<View style={styles.container}>{view}</View>);
}
renderGallery() { //onPress={() => this.setState({showGallery: !this.state.showGallery})}
return (
<GalleryView style={styles.overlaycontainer} onPress={this.toggleView.bind(this)}>
</GalleryView>
);
}
render() {
console.log("LOG: GalleryView render function called");
console.log("FlatList data: " + this.state.testData.toString());
return(
<FlatList
data={this.state.testData}
keyExtractor={this._keyExtractor}
style={styles.container}
renderItem={
({item, index}) => {
this._renderImageView(item, index);
}
}
/>
);
}
}
_keyExtractor = (item, index) => index;
_renderImageView = (item, index) => {
console.log("LOG: renderItem: " + item);
return(
<Text style={{borderColor: "red", fontSize: 30, justifyContent: 'center', borderWidth: 5, flex: 1}}>{item}</Text>
);
}
//testData: ["item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9"]
I'm fairly confident this isn't some flex issue, but in case I missed something here's the relevant stylesheets too:
const styles = StyleSheet.create({
container: {
flex: 1
},
overlaycontainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between'
},
});
So, what I'm expecting to happen is to see a list of text items.
What's happening instead is I see a white screen with nothing on it.
Why is this list not rendering?
You can try replicating this if you want, define FlatList inside your return().
<View>
<FlatList
data={Items}
renderItem={this.renderItems}
enableEmptySections
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
Then declare your Items inside constant(array of objects) outside your class like this,
const Items = [
{
id: FIRST,
title: 'item1',
},
{
id: SECOND,
title: 'item2',
},
{
id: THIRD,
title: 'item3',
},
// and so on......
];
After this get your items outside render by calling a function
onSelection = (item) => {
console.log(item.title)
}
renderItems = ({ item }) => {
return(
<TouchableOpacity
onPress={() => this.onSelection(item)}>
<View>
<Text>
{item.title}
</Text>
</View>
</TouchableOpacity>
)
}
Are you not missing the return on your renderItem?
render() {
console.log("LOG: GalleryView render function called");
console.log("FlatList data: " + this.state.testData.toString());
return(
<FlatList
data={this.state.testData}
keyExtractor={this._keyExtractor}
style={styles.container}
renderItem={
({item, index}) => {
return this._renderImageView(item, index);
}
}
/>
);
}
}