React Native editable FlatList – How to edit item data in a List?
Hi code community, I am new in reactive native and I need some help. I am trying to handle a data update in FlatList. where the user can change the text name for whatever they want. Exemplo if the user creates a folder named "Bar" but later on decides to change the name for "Bar1". How can I do it??? Here is my code so far...
My Modal State
const [modalVisible, setModalVisible] = useState(false);
My FlatList Component... discard data={folderToDisplay}
<FlatList numColumns={4} data={folderToDisplay} renderItem={renderItem} />
My renderItem={renderItem} is set like this: where {itemData.item.value} is here I am fetching my text name. We can replace {itemData.item.value} to Bar if you wish.
const renderItem = (itemData) => {
return (
<View style={styles.folderNameContainer}>
<TouchableOpacity
onPress={() => {
setModalVisible(true);
}}
>
<Text style={styles.itemText}>{itemData.item.value}</Text>
</TouchableOpacity>
</View>
);
};
When I press the text name a Modal opens.
<Modal animationType="fade" transparent={true} visible={modalVisible}>
<SimpleModal
closeModal={onCloseModal}
/>
</Modal>
...And when I press cancel it close.
const onCloseModal = () => {
setModalVisible(false);
};
My SimpleModal component is set like this: I could make my code all in one place but it looks ugly.
const SimpleModal = (props) => {
return (
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Change Name!</Text>
<TextInput
style={styles.textInput}
placeholder="Type Here..."
maxLength={15}
/>
<View style={styles.saveAndCancelContainer}>
<View style={styles.button}>
<Button title="Save" color="#87CEEB" onPress={props.renameFolder} />
</View>
<View style={styles.button}>
<Button title="Cancel" color="red" onPress={props.closeModal} />
</View>
</View>
</View>
</View>
);
};
Now the goal here is in my TextInput the user type a new name and then press Save. When onPress it will update the name for the new name. and close the modal. So far this is what I have:
<Modal animationType="fade" transparent={true} visible={modalVisible}>
<SimpleModal
renameFolder={handleRenameFolder}
/>
</Modal>
const handleRenameFolder = () => {
///....????
setModalVisible(false);
};
Related
const Screen = () => {
return (
<View style={styles.container}>
<Text style={styles.heading}>Screen Data</Text>
<View>
<FlatList
data={data}
renderItem={({item}) => (
<View>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description}>{item.description}</Text>
</View>
)}
ListFooterComponent={() => <Buttons text={''} onPress={undefined} />}
/>
</View>
</View>
);
};
export default Screen;
I have created a Button component which always come at the end of flatlist which is scrollable but it gets overlap I want it to keep at end of content.
List footer component is made for that it can be any component, check docs https://reactnative.dev/docs/flatlist#listfootercomponent.
I am implementing my own Modal, trying to replace the Alert.alert with something more beautiful. I made it to be displayed when needed, but it is not hiding on the button press, but I think I transferred it the needed function. My modal structure is the following:
export const RCModal = ({ title, visible, onButtonPress }) => {
return (
<Modal
animationType='fade'
transparent={true}
visible={visible}
>
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<Pressable style={styles.button} onPress={onButtonPress}>
<Text style={styles.text}>OK</Text>
</Pressable>
</View>
</Modal>
)
};
And it is used in the application in the following way:
// ...
const [alertVisible, setAlertVisible] = useState(false);
const [alertTitle, setAlertTitle] = useState();
const [alertOnPress, setAlertOnPress] = useState();
// ...
const winner = (theWinner) => {
setBlocked(true);
setAlertTitle(`${theWinner} win!`);
setAlertOnPress(() => setAlertVisible(!alertVisible));
setAlertVisible(true);
}
// ...
return (
<View style={styles.container}>
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={alertOnPress} />
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
<Text style={styles.title}>Noughts and Crosses</Text>
<Text style={styles.tip}>Get {winCondition()} in row, column or diagonal</Text>
<View style={styles.buttonsContainer}>
<Text style={styles.turnContainer}>Turn: <Text style={[styles.turn, { color: turn === 'X' ? '#2E86C1' : '#E74C3C'}]}>{turn}</Text></Text>
<TouchableHighlight underlayColor="#000000" style={[styles.button, styles.newGameButton]} onPress={setInitialFieldState}>
<Text style={styles.buttonText}>New game</Text>
</TouchableHighlight>
</View>
<Field state={fieldState} size={fieldSize} onCellPress={onCellPress} />
</ScrollView>
<View style={styles.settingsButtonContainer}>
<TouchableHighlight underlayColor={theme.colors.secondary} style={styles.settingsButton} onPress={onSettingsPress}>
<Image source={require('../img/settings.png')} style={styles.settingsIcon} />
</TouchableHighlight>
</View>
</View>
);
};
When the winner() is called, it is displayed as it should, but when I press OK button, it is not hiding. How can I fix it?
You can use setAlertVisible to change the alertVisible state:
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={() => setAlertVisible(false)} />
The answer was that to set a function like a state variable, I needed to set it like
setAlertOnPress(() => () => setAlertVisible(false))
(2 x () =>)
I'm new to React. I'm having a tough time trying to change how a screen looks depending on what value I select inside a dialog/modal that pops up when I press the header button using a Stack Navigator.
This is a gif of what it looks like: https://i.imgur.com/Yw2jbHu.gifv
This is what my Stack Navigation looks like:
const HistoryStack = () =>{
return (
<Stack.Navigator screenOptions={{headerShown:true,headerStyle:{backgroundColor:'#21252B',shadowColor: '#333842'},headerTintColor:"#528bff",headerTitleAlign: 'center' }}>
<Stack.Screen name="History" component={History} />
<Stack.Screen name="Logging" options={({ navigation, route })=> ({headerTitle: props => <SwitchWorkoutButton/>})}component={Logging} />
</Stack.Navigator>
);
}
As you can see I made the header title my own custom button component. I added a currentWorkout state variable to the component that gets set whenever a value selected in the dialog.
This is what the header button looks like:
const SwitchWorkoutButton = () =>{
const [showDialog,setShowDialog] = useState(false)
const [currentWorkout,setCurrentWorkout] = useState('A')
const renderDialog = (showDialog) =>{
return (<Dialog migrate visible={showDialog} useSafeArea bottom onDismiss={setShowDialog(false)}>test</Dialog>)
}
const closeDialog = () =>{
setShowDialog(false)
}
const openDialog = () =>{
setShowDialog(true)
}
return (
<View>
<Chip label ="Workout A"
iconStyle={{margin: 4,tintColor:'#528bff',width:11,height:6}}
rightIconSource={require('../assets/chevronDown.png')}
labelStyle={{color: Colors.white,fontSize:13}}
containerStyle={{borderColor: "#2D2E33", backgroundColor: "#2D2E33", marginLeft: Spacings.s3,padding:2.5,width:115}}
onPress={() =>{ console.log('pressed',showDialog)
setShowDialog(true)
console.log('pressed',showDialog)
}}/>
<Dialog migrate height={150} containerStyle={styles.roundedDialog} panDirection={PanningProvider.Directions.UP} visible={showDialog} onDismiss={closeDialog} useSafeArea top>
<View style ={{backgroundColor:'#2d2e33',borderBottomColor:'#929497',borderBottomWidth:.2,alignItems:'center',}}>
<Text style={{fontSize:20,marginVertical:10,color:'#929497'}}>Switch Workout</Text>
</View>
<TouchableOpacity onPress={()=>{setCurrentWorkout('B')}}>
<View style ={{paddingLeft:20,marginVertical:5,marginTop:10}}>
<Text style={{fontSize:20,color:'#929497',}}>Workout B</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{setCurrentWorkout('C')}}>
<View style ={{paddingLeft:20,marginVertical:5}}>
<Text style={{fontSize:20,color:'#929497'}}>Workout C</Text>
</View>
</TouchableOpacity>
</Dialog>
</View>
);
}
And this is the page where the content is displayed through my WorkoutSet component that gets passed a prop that I will use to display the correct data.
const Logging = ({navigation}) =>{
const [selectedIndex,setSelectedIndex] = useState(0)
return (
<SafeAreaView style={{ flex: 1,backgroundColor:'#21252B'}}>
<View style={{ flex:3}} >
<WorkoutSet id={'A'}/>
</View>
</SafeAreaView>
);
}
My question is how do I grab the value I selected in the dialog to do that. I've read through https://reactnavigation.org/docs/header-buttons/ but their use case is different than mine.
I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>
I am using React Native FlatList and React Native Modal.
Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).
However, if there are 4 items in the FlatList, selecting 1 item causes
all 4 modals to pop up.
Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?
Code Snippet below (some lines of code were removed as it's not needed):
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false,
}
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
viewModal(item, price) {
const { modalVisible } = this.state;
return (
<Modal
statusBarTranslucent={true}
animationType={"slide"}
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View>
<View>
<View>
<Text>
Appointment Start Time:
</Text>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
Appointment End Time:
</Text>
<Text>
{moment(item.end_start).format('h:mm a')}
</Text>
</View>
<View style={styles.row}>
<Text>
Total:
</Text>
<Text>
{price}
</Text>
</View>
<View>
<View>
<Button
mode="outlined"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Cancel'}
</Button>
</View>
<View>
<Button
mode="contained"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Accept'}
</Button>
</View>
</View>
</View>
</View>
</Modal>
);
}
viewFreelancerTime() {
return (
<View>
<FlatList
renderItem={({ item }) => {
let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
return (
<Container>
{this.viewModal(item, totalPrice)}
<TouchableNativeFeedback
onPress={() => {
this.setModalVisible(true);
}}
>
<View>
<View>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
{totalPrice}
</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
}}
/>
</View>
);
}
render() {
return (
<>
<View style={{ flex: 1 }}>
{this.viewFreelancerTime()}
</View>
</>
);
};
The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.
To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.
UPDATE
Just something like this:
import React, {useState} from "react";
import { Modal } from "react-native";
export default function MyFlatList(props) {
const [selectedItem, setSelectedItem] = useState(null);
const handleOnSelectItem = (item) => {
setSelectedItem(item);
};
const handleOnCloseModal = () => {
setSelectedItem(null);
};
renderItem = ({ item }) => {
return (
<Container>
<TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
<View>
<View>
<Text>{moment(item.time_start).format("h:mm a")}</Text>
</View>
<View>
<Text>{totalPrice}</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
};
return (
<View>
<FlatList renderItem={this.renderItem} />
<CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
</View>
);
}
export function CustomModal(props) {
const { isVisible, item, onClose, /*...*/ } = props;
// Play with the item data
let totalPrice = (
parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
).toFixed(2);
return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}
I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.
Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible