What I did here so far is, just created three random views and with the help of the state, I just hide/show the view. However, according to requirements that I have, the logic is the following:
If you click to any close icon (x) it should disappear and in place of this, the view which is standing in the bottom should replace it. Like in Gmail, if you archive message it will be archived and will disappear. The view in the bottom will fill that place.
Can anyone share, small example in order to figure out concept. I understand here that task should be done with array and view should be displayed with a help of map=>. I am bit in confusion since this task requirement concept is brand new to me.
I created for you a small example in which we display cards and when you click on an item, that item is removed.
The code and the working example can be found in the followig link
const App = () => {
const items = ['Item1', 'Item2', 'Item3']
const [visible, setVisible] = useState({
item0: true,
item1: true,
item2: true
});
const itemClicked = (item) => {
setVisible({
...visible,
[item]: false
})
}
return (
<View style={styles.container}>
{
items.map((item, i) => {
const itemRendered = `item${i}`;
return (
<View>
{
visible[itemRendered] &&
<TouchableOpacity onPress={() => itemClicked(itemRendered)} style={styles[itemRendered]}>
<Text>{item}</Text>
</TouchableOpacity>
}
</View>
)
})
}
</View>
);
}
I used the map to render 3 clickable items. Those will be displayed only if they should be displayed based on the value of visible[itemRendered]. The component keeps the state of which items should be visible or not. In case you click on an item, the state is updated so as the clicked item wont be displayed.
Related
I have a problem where useState updates the state but does not show the changes until I refresh the app. First, I declare an array called sampleFriends, made up of objects with fields "name", "location", and "picture" (each element of the array looks similar to: {name: 'John', location: 'Boston', picture: TestImage}).
Then, I have the following useState:
const [selectedFriends, setSelectedFriends] = useState([])
At some point, I successfully render
sampleFriends.map(({ name, location, image }, index) => (
<NewMsgTableRow
name={name}
index={index}
location={location}
image={image}
onPress={() => selectFriend(name)}
/>
))
And I also have this function right above
const selectFriend = name => {
// if the friend is not already selected
if (!selectedFriends.find(e => e === name)) {
const newFriends = selectedFriends
newFriends.push(name)
setSelectedFriends(newFriends)
}
}
The component NewMsgTableRow has a button that uses onPress
<TouchableOpacity
onPress={onPress}
>
So, I want to render selectedFriends as soon as they are selected (the TouchableOpacity is touched and thus the state updates). However, when I click the button, nothing shows up until I edit and save my code and it refreshes automatically. It was my understanding that useState rerendered the components as soon as it was updated, but it is not happening in this case and I can't figure out why. I've been reading that it is async and that it does not change it instantly, but I don't know how to make it work. Hope it makes sense and thanks for your help!!
You can use array spread or Array.concat() to make a shallow clone, and add new items as well) so change the line below:
const newFriends = selectedFriends
to this line :
const newFriends = [...selectedFriends]
I found myself stupid and could not get my head around with the logic.
I would like to show a few info triggered byonClick, and only those with matched id.
For example, if I click on the button with id of 1, it would only want to show values in that specific object with id:1 like description, library, etc. Right now, all the data are displayed, and because I am using component in material ui, every drawer component are displayed on top of each other (overlapping).
I know the reason causing this is because I have the drawer component inside the map method, but what could be potential solution?
Below are my simple code,
The structure of my data looks like this,
export const projectdata = [
{
id: 1,
title: "",
subtitle: "",
thumbnail: "",
description:
"",
tech: [
],
library: [""],
WebsiteUrl: "",
GitHubUrl: "",
},
...more data with sequential id number...
]
Original:
const handleDrawerOpen = (event) => () => {
setOpen(!open);
};
...
...
<SwipeableDrawer
open={open}
onClose={handleDrawerOpen(false)}
onOpen={handleDrawerOpen(true)}>
***...only show data with matched id...***
</SwipeableDrawer>
I have already map the array to display the data on webpage inside a div like this,
<div>
{ projectdata?.map(({ id, title, subtitle, thumbnail, description, tech, WebsiteUrl, GitHubUrl, library, index }) => (
<>
<Card>
...some info displayed here...
<button onClick={handleDrawerOpen(id)}></button>
</Card>
<SwipeableDrawer>
***...only show data with matched id...***
</SwipeableDrawer>
</>
))}
<div>
One solution I can think of:
with useState(), pass in id as a prop
const [projectDetailId, setProjectDetailId] = useState(null);
const [projectDetailPage, setProjectDetailPage] = useState(false);
const handleDrawerOpen = (id) => {
setProjectDetailId(id);
setProjectDetailPage(true);
};
const handleDrawerClose = () => {
setProjectDetailId(null);
setProjectDetailPage(false);
};
...
...
{projectDetailId === id ?
<SwipeableDrawer
open={projectDetailPage}
onClose={handleDrawerClose}
></SwipeableDrawer>
: null
}
However, this will trigger strange behavior of the drawer (lagging and no animation), especially with mobile device.
Possibly due to this logic projectDetailId === id ? true : false.
Ok, after your update, your problem is that you create multiple drawers, one for each id. When you click on open and set the open prop to true, all your drawers use this same prop so they all open.
You should move the Drawer out of the for and only create one, and send your object that has the id as a prop to the content of the drawer you have.
something like:
const handleDrawerOpen = (yourData) => () => {
setOpen(!open);
setYourData(yourData)
};
...
// and somewhere on your code
<SwipeableDrawer open={open}>
<SomeComponentToShowTheData data={yourData}/>
</SwipeableDrawer>
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 tried Googling this but due to the English language not being my greatest skill I find it hard to Google the right thing. I've tried to fix this problem myself which works, but I wonder if there are better ways to do this.
Situation:
I have a json object and want to render the values in it, so I mapped the values and render it in a component. I only have to call that component once and it will render itself multiple times acoording to how many objects are in my json object.
Now when I fire the onPress function on a single, specific rendered instance, it will fire for every rendered instance.
Desired situation:
When I have multiple rendered instances of my component, and I fire onPress on a single one, it should only fire for that instance.
Code:
I'm performing a get request and response returns a json object:
{
"objects": [
{
"name": "Alarm chauffeurs#ON=100",
"object_id": 1,
"input_value": 0,
"last_changed": "2019-03-08T14:30:54",
}, ...
Next I map the values to my component:
let cards = this.state.dataSource.objects.map((val, key) => {
return (
<Animated.View key={key} style={[cardStyle.container, { height: this.state.collapse[parseInt(val.object_id)] }]}>
<TouchableHighlight onPress={() => this.toggle(val.object_id)} />
</Animated.View>
);
});
Now I have to call {cards} in my MainView only once and React Native will render multiple cards:
return (
<View style={style.container}>
<View style={style.colDash}>
<ScrollView>
{cards}
</ScrollView>
</View>
</View>
);
Here's an image of the output
Now once I press a button, onPress gets fired on every component on screen.
I've tried:
Created an array and pushed a random value to it as id always starts at 1. The rest is done with a simple for loop. This probably isn't the best way to handle my problem.
toggle(id) {
var test = []
test.push(123);
for (var i = 1; i < 8; i++) {
if (id == i) {
var temp = new Animated.Value(75);
Animated.timing(
temp, {
toValue: 150,
duration: 500,
}
).start()
test.push(temp);
}
else {
var temp = new Animated.Value(75);
Animated.timing(
temp, {
toValue: 75,
duration: 1,
}
).start()
test.push(temp);
}
}
this.setState({
collapse: test
});
Later on, I also want to add the possibility to be able to toggle multiple instances at the same time. As of now when I fire one onPress, the rest gets reset.
Add an index to your TouchableHighlight component and try then
I have a Flatlist which works like a To Do list with a filter for “ToDo” and “Upcoming”. When a user swipes to complete the item, it gets hidden from the list by changing a displayIndex attribute. I would like this to reload the list after the swipe or before the user selects “Upcoming”. After reading through other stack overflow answers I have tried adding extraData={this.state} (and creating a this.state.refresh property which changes after every swipe) to the Flatlist and I also ensured that the list items themselves are React.Components and not PureComponents. I have also tried two ways to hide the ListItems, conditionally rendering them and conditionally changing the style to hidden. Still, I am not seeing any change in my Flatlist.
Below is some partial code to see if there are any gotchas I missed:
In the MainScreen.js
async _addCompletion(myItem) {
//Lots of business logic and after it's done the below code activates
await AsyncStorage.setItem(myItem.key, JSON.stringify(myItem));
await this._updateData();
this.setState({ refresh: !this.state.refresh });
}
render() {
const buttons = ['To Do', 'Upcoming'];
const { displayModeIndex } = this.state;
return (
<View>
<ButtonGroup
onPress={this._updateButtonIndex}
buttons={buttons}
selectedIndex={displayModeIndex}
/>
<FlatList
displayMode={this.state.displayModeIndex}
data={this.state.data}
extraData={this.state}
scrollEnabled={this.state.scrollEnabled}
renderItem={({ item }) => (
<MyListItem
myListItem={item}
addCompletion={this._addCompletion}
displayIndex={this.state.displayModeIndex}
setScrollEnabled={this._setScrollEnabled}
navigation={this.props.navigation}
/>
)}
/>
</View>
);
}
In MyListItem.js
_displayMyItem {
//Logic that determines whether to display a myItem based on several factors. I can confirm this works after refreshing.
}
_hideMyItem = () => {
Animated.timing(this.containerHeight, {
toValue: 0,
}).start(() => {
this.setState({ hidden: true });
});
};
render () {
const {myItem} = this.state;
//Other code that determines how the list item looks depending on myItem data.
return (
//I have also tried to return null if this._displayMyItem(this.state.myItem) returns false
<View style={!this._displayMyItem(this.state.myItem) && { display: 'none' }}>
<Swipeable
onPress={this._onPressRow}
setScrollEnabled={this.props.setScrollEnabled}
addCompletion={this.props.addCompletion}
hideMyItem={this._hideMyItem}
myItem={this.state.myItem}
>
//Other JSX Code
</View>
)
}
The Swipeable is a custom component that calls addCompletion after a swipe and _hideMyItem after everything is done. It is not a PureComponent either.
There's a lot going on here, so I've only included code that seems relevant. I can add more if needed. The addCompletion method is a long
would help some captures...
When you swipe the item , it's just empty right?, if it leaves an empty space try this way of conditional rendering , idk if it would work.
in MyListItem.js
render () {
const {myItem} = this.state;
//Other code that determines how the list item looks depending on myItem data.
return (
//I have also tried to return null if this._displayMyItem(this.state.myItem) returns false
{!this.state.hidden?
<View style={!this._displayMyItem(this.state.myItem) && { display: 'none' }}>
<Swipeable
onPress={this._onPressRow}
setScrollEnabled={this.props.setScrollEnabled}
addCompletion={this.props.addCompletion}
hideMyItem={this._hideMyItem}
myItem={this.state.myItem}
>
//Other JSX Code
</View>:null}
)
}
wich checks if this.state.hidden is false , returns the component, else, returns null