I#m making this React Native Calendar app where user can save tasks on specific dates. Using the react-natuive-calendar api from this git: https://github.com/wix/react-native-calendars
The user get to log in the app, and the task are saved as object list. The problem I'm facing is that when i press on task an alert is shown, if the user press 'Yes' the task should be removed from the list, and after 2 sec the list should be refreshed. But in order for this to work i have to do it twice. Long story short, why do i have to run the same function twice in order for this to work?
//function that takes in the date of task and REMOVE it
const RemoveTask = (taskDate) => {
GloballyConst.tasks[taskDate] = [];
SetNewItems();
//list of task refreshing after 3 sec
const interval =setInterval(() => {
setRefreshing(true);
}, 3000)
}
//on Task clicked alert pop up to confirm removing
const createTwoButtonAlert = () =>
Alert.alert('Remove Task', 'Are you sure you want to delete'+ {clickedDate}, [{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{text: 'Yes', onPress: () => {
//Function to remove task
RemoveTask(clickedDate);
}},
]);
//this is the way task will look like
const renderItem = (item) => {
return (
<TouchableOpacity
onPress={() => {
//set cliked date is set to the date of the object
setClickedDate(item.date)
//colling the function that show allert
createTwoButtonAlert()
}}
style={styles.taskCont}>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text style={{color:'#FFFFFF'}}>{item.name}</Text>
<Image style={styles.icon} source={require('../assets/iconsHome/important.png')}/>
</View>
<View style={{flexDirection:'row', alignItems: 'center'}}>
<Image style={styles.iconSmall} source={require('../assets/iconsHome/clock.png')}/>
<Text style={{color:'rgba(255,255,255,0.5)', padding:5}}>{item.timeStart}</Text>
</View>
</TouchableOpacity>
);
}
The list looks like this:
module.exports = {
//this is the list of task and get items from addTaskScreen
tasks: {
'2023-02-20': [{name: 'Make Youtube Video', timeStart: '12:00', date: '2023-02-20'}],
}
};
After runing the function once and console out the tasks, the task that should be removed is still there, and if i run in again it's gone.
!SOLVED!
The problem was the way the Alert function is called.
before:
const renderItem = (item) => {
return (
<TouchableOpacity
onPress={() => {
//set cliked date is set to the date of the object
setClickedDate(item.date)
//colling the function that show allert
createTwoButtonAlert()
}}
style={styles.taskCont}>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text style={{color:'#FFFFFF'}}>{item.name}</Text>
<Image style={styles.icon} source={require('../assets/iconsHome/important.png')}/>
</View>
<View style={{flexDirection:'row', alignItems: 'center'}}>
<Image style={styles.iconSmall} source={require('../assets/iconsHome/clock.png')}/>
<Text style={{color:'rgba(255,255,255,0.5)', padding:5}}>{item.timeStart}</Text>
</View>
</TouchableOpacity>
);
after:
const renderItem = (item) => {
return (
<TouchableOpacity
onPress={() => {
//set cliked date is set to the date of the object
setClickedDate(item.date)
//colling the function that show allert
createTwoButtonAlert(item.date)
}}
style={styles.taskCont}>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text style={{color:'#FFFFFF'}}>{item.name}</Text>
<Image style={styles.icon} source={require('../assets/iconsHome/important.png')}/>
</View>
<View style={{flexDirection:'row', alignItems: 'center'}}>
<Image style={styles.iconSmall} source={require('../assets/iconsHome/clock.png')}/>
<Text style={{color:'rgba(255,255,255,0.5)', padding:5}}>{item.timeStart}</Text>
</View>
</TouchableOpacity>
);
Related
I'm trying to select multiple options from the map function but i cant seem to generate logic to do so, i'm selecting a button and that button is generating a heading and a text felid in a scrollview with the lable of button as heading, but now i want to select multiple buttons to generate multiple components:
Any logic and help would be great please, m stuck!
here is code for refrence:
this is usestate and buttonLabels array:
const handlePress = (index) =>
{
setSelectedButtonIndex(index === selectedButtonIndex ? null : index);
};
const buttonLabels = [
'Homeowner',
'In-depth',
'Inherited',
'Probate',
'Lost income',
'Relocation',
'Divorce',
'Downsizing'
];
and here is the map function:
{/* List of Items */}
<View style={styles.buttonGrid}>
{buttonLabels.map((label, index) => (
<TouchableOpacity
key={index}
style={[
styles.button,
{ backgroundColor: index === selectedButtonIndex ? '#346284' : '#FFFFFF' },
]}
onPress={() => handlePress(index)}
>
<Text style={{ color: index === selectedButtonIndex ? '#FFFFFF' : '#888889' }}>
{label}
</Text>
</TouchableOpacity>
))}
</View>
and here i'm generate component at the selectedbutton
<ScrollView style={{flexGrow:2}}>
{selectedButtonIndex !== undefined && (
<View style={styles.headingContainer}>
<View style={styles.heading}>
<Text style={styles.headingText}>{buttonLabels[selectedButtonIndex]}</Text>
<TouchableOpacity onPress={() => setSelectedButtonIndex(undefined)}>
<Image source={require('../assets/Icons/close.png')} style={styles.closeIcon} />
</TouchableOpacity>
</View>
<TextInput style={styles.textInput} placeholder="Enter Text Here" />
</View>
)}
</ScrollView>
if you want to choose multiple item,
let array=[{"option":"option1",selected:false}, {"option":"option1",selected:false}]
based on index or item you need to change the boolean value, then show when the boolean value is true show the item example ,
onpress function like
const onselect=(selectedIndex)=>{
let temp=array //local array
temp.map((item,index)=>{
if(index==selectedIndex) item.selected=!item.selected
})
setState([...temp]) //set your local state
})
in render method,
array.map((item,index)=>{if(item.selected) <View></View>})
I doubt this is possible but I'd like to hear your thoughts.
I am getting an array which the number of values are unknown.
And I am using a for loop to spread it in a state:
const { letters,number } = route.params
const [arr,setArr] = useState([])
useEffect(() => {
var eachArr = []
setArr([])
for (let i = 1; i < Number(number) + 1; i++) {
eachArr.push(i)
}
setArr(eachArr)
}, [])
Now I use the state to map through to render accordingly
return (
<View style={styles.container}>
<ScrollView>
{
arr.length > 0 && arr.map(num => (
<View style={styles.placeContainer} key={num}>
<View style={styles.place}>
<Text style={{ fontWeight:'bold',letterSpacing:1 }}>{num} {letters}</Text>
<Feather name="check-circle" size={24} color="grey" />
</View>
<View style={{ marginVertical:10 }}>
<Progress color={colors.purple} progress={1} width={null} borderWidth={0} />
</View>
<View style={styles.button}>
<Text style={{ color:colors.white,fontFamily:'viga' }}> this {letters}</Text>
</View>
<View style={{...styles.button,backgroundColor:colors.lightPurple}}>
<Text style={{ color:colors.white,fontFamily:'viga' }}> this {letters}</Text>
</View>
</View>
))
}
</ScrollView>
</View>
)
I now have a problem. I have two buttons on each of the mapped element/component as you can see above.
I need to listen for a click on one and deactivate the clicked button and then activate the other button.
The problem is that I do not know how to set/update a state based on the which button is clicked since the number of rows are unknown.
I know I can call a function:
const callFunc = (val) -> {
console.log(val)
}
And call the function when a respective button is clicked and pass the val or sth. While this solves some part of the issue, how do I then deactivate it and active the new button when I didn't set a state for it
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 am not sure how to add a delete function in a FlatList. I know I can make different components, but I want to know how to do it within this one file. I've trying to figure this out for hours, but do not know how to do.
export default function test() {
const [enteredGoal, setEnteredGoal] = useState("");
const [courseGoals, setCourseGoals] = useState([]);
const goalInput = enteredText => {
setEnteredGoal(enteredText);
};
const addGoal = () => {
setCourseGoals(currentGoals => [
...currentGoals,
{ key: Math.random().toString(), value: enteredGoal }
]);
};
const removeGoal = goalId => {
setCourseGoals(currentGoals => {
return currentGoals.filter((goal) => goal.id !== goalId);
})
}
return (
<View style={styles.container}>
<View>
<TextInput
color="lime"
style={styles.placeholderStyle}
placeholder="Type here"
placeholderTextColor="lime"
onChangeText={goalInput}
value={enteredGoal}
/>
</View>
<FlatList
data={courseGoals}
renderItem={itemData => (
<View style={styles.listItem} >
<Text style={{ color: "lime" }}>{itemData.item.value}</Text>
</View>
)}
/>
<View>
<TouchableOpacity>
<Text style={styles.button} onPress={addGoal}>
Add
</Text>
</TouchableOpacity>
</View>
</View>
);
}
You just need to modify your code a bit to handle the delete button. Since you already have delete functionality, call that function when you click the delete button. That's it.
<FlatList
data={courseGoals}
renderItem={itemData => (
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
<Text style={{ color: "lime" }}>{itemData.item.value}</Text>
<TouchableOpacity onPress={() => removeGoal(itemData.item.key)}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
)}
/>;
EDIT
change your removeGoal function as below
const removeGoal = goalId => {
setCourseGoals(courseGoals => {
return courseGoals.filter(goal => goal.key !== goalId);
});
};
Hope this helps you. Feel free for doubts.
I have a flat list, which gets its data source as a state. Actually, this data is from firebase, and i have been using redux. So, the data is fetched in the actions, and using callback i get the data to state.
What i want to achieve is, when there is no data found from the api, An empty list message should be show in the view. Actually , i achieved this using "ListEmptyComponent". But whats happening is the screen starts with empty message, and the spinner loads below it, and then if data found the message goes away as well as spinner.
But, what i wanted is, when the view gets rendered the first thing everyone should see is the spinner, and then if data empty spinner hides then empty list message displays.
How to achieve this ?
My Action :
export const fetchOrderHistory = (phone, callback) => {
return (dispatch) => {
dispatch({ type: START_SPINNER_ACTION_FOR_ORDER_HISTORY })
firebase.database().ref('orders/'+phone)
.on('value', snapshot => {
const snapShotValue = snapshot.val();
callback(snapShotValue);
dispatch ({ type: ORDER_HISTORY_FETCHED , payload: snapshot.val()});
dispatch({ type: STOP_SPINNER_ACTION_FRO_ORDER_HISTORY })
});
};
};
My Flat List & spinner:
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card
containerStyle={{ borderRadius: 5 }}
>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }
>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</Card>
)}
/>
{this.props.isSpinnerLoading &&
<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> }
My Call back at componentWillMount which set state:
componentWillMount() {
this.props.fetchOrderHistory((this.props.phone), (snapShotValue)=> {
const userOrderHistory = _.map(snapShotValue, (val,uid) => ({uid, ...val}))
this.setState({ historyOfOrders: userOrderHistory })
});
}
My EmptyList Message:
onListEmpty = () => {
return <View style={{ alignSelf: 'center' }}>
<Text style={{ fontWeight: 'bold', fontSize: 25 }}>No Data</Text>
</View>
}
My State:
state = { historyOfOrders: "" }
I am getting the spinner values from the reducers, using mapStateToProps.
Kindly Guide me, through
you have to do two things for that.
First, show Flatlist only if the loader is stopped. Second, set default value of this.state.historyOfOrders is null and check if this.state.historyOfOrders not null then only show Flatlist.
Here is a code:
{(!this.props.isSpinnerLoading && this.state.historyOfOrders != null) ?
(
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card containerStyle={{ borderRadius: 5 }}>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</Card>
)}
/>
) : null
}
With this condition, even if you want loader above Flatlist you can do that.
The path you should take is rendering only the spinner when the loading flag is set and rendering the list when loading flag is false.
Your render method should be like below
render()
{
if(this.props.isSpinnerLoading)
{
return (<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> );
}
return (/** Actual List code here **/);
}