Set Selected items in react native multi select - react-native

I want to select the selected items which i already select & save at backend after that if i get response then set already selected those items which i have set in react native multi select
here is my code for react native multiselect
//Example Multiple select / Dropdown / Picker in React Native
import React, { Component } from "react";
//Import React
import { View, Text, Picker, StyleSheet, SafeAreaView } from "react-native";
//Import basic react native components
import MultiSelect from "react-native-multiple-select";
//Import MultiSelect library
//Dummy Data for the MutiSelect
this.items = [
{ id: "1", name: "America" },
{ id: "2", name: "Argentina" },
{ id: "3", name: "Armenia" },
{ id: "4", name: "Australia" },
{ id: "5", name: "Austria" },
{ id: "6", name: "Azerbaijan" },
{ id: "7", name: "Argentina" },
{ id: "8", name: "Belarus" },
{ id: "9", name: "Belgium" },
{ id: "10", name: "Brazil" }
];
export default class App extends Component {
state = {
//We will store selected item in this
selectedItems: [
{ id: "1", name: "America" },
{ id: "2", name: "Argentina" },
{ id: "3", name: "Armenia" },
{ id: "4", name: "Australia" }
]
};
onSelectedItemsChange = selectedItems => {
this.setState({ selectedItems });
//Set Selected Items
};
render() {
const { selectedItems } = this.state;
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, padding: 30 }}>
<MultiSelect
hideTags
items={items}
uniqueKey="id"
ref={component => {
this.multiSelect = component;
}}
onSelectedItemsChange={this.onSelectedItemsChange}
selectedItems={selectedItems}
selectText="Pick Items"
searchInputPlaceholderText="Search Items..."
onChangeInput={text => console.log(text)}
tagRemoveIconColor="#CCC"
tagBorderColor="#CCC"
tagTextColor="#CCC"
selectedItemTextColor="#CCC"
selectedItemIconColor="#CCC"
itemTextColor="#000"
displayKey="name"
searchInputStyle={{ color: "#CCC" }}
submitButtonColor="#48d22b"
submitButtonText="Submit"
/>
<View>
{this.multiSelect &&
this.multiSelect.getSelectedItemsExt(selectedItems)}
</View>
</View>
</SafeAreaView>
);
}
}

Got it.
this.state={
selectedData: "158" // list of id here(without space, that is your response of api)
};
<MultiSelect
.....
.....
selectedItems={selectedData}
/>
<View>
{this.multiselect ? this.multiSelect.getSelectedItemsExt(selectedData): null}
</View>

I had this same issue and I figured out how to preset selections by using parts of the accepted answer.
I learned that selectedItems is a list of whatever is set as the uniqueKey in the MultipleSelect props.
For example, if you put the uniqueKey as a numbered id of each list member and you wanted the second member set as selected, you would have selectedItems = {[ "2" ]}
I hope this makes sense.

Related

React Native List Map method selected item scroll to the middle of ScrollView

`
import * as React from 'react';
import { Text, View, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
import { useState } from "react";
import {useRef,createRef} from 'react';
import { useEffect } from 'react';
import { Dimensions } from 'react-native';
const windowWidth = Dimensions.get('window').width;
export default function App() {
const [selectedId, setSelectedId] = useState(null);
const elementsRef = useRef(persons.map(() => createRef()));
const [y, setY] = useState(false);
return (
<View style={{
flex:1,
justifyContent:'center',
alignItems:'center',
}}>
<ScrollView contentContainerStyle={{
paddingHorizontal:20
}}>
<View>
{persons.map((person, index) => {
const backgroundColor = person.id === selectedId ? "#6e3b6e" : "#f9c2ff";
const onPress = (newRef) => {
newRef?.current?.measureInWindow( (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to frame: ' + fx)
console.log('Y offset to frame: ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
setY(fy)
})
setSelectedId(person.id)
}
return (
<TouchableOpacity
ref={elementsRef?.current[index]}
key={person.id}
style={{
backgroundColor:backgroundColor,
marginBottom:20,
height:200,
justifyContent:'center',
alignItems:'center',
width: windowWidth-40
}}
onPress={() => onPress(elementsRef?.current[index])} >
<Text>{person.name}</Text>
<Text>{y}</Text>
</TouchableOpacity>
);
})}
</View>
</ScrollView>
</View>
);
}
const persons = [
{
id: "1",
name: "Earnest Green",
},
{
id: "2",
name: "Winston Orn",
},
{
id: "3",
name: "Carlton Collins",
},
{
id: "4",
name: "Malcolm Labadie",
},
{
id: "5",
name: "Michelle Dare",
},
{
id: "6",
name: "Carlton Zieme",
},
{
id: "7",
name: "Jessie Dickinson",
},
{
id: "8",
name: "Julian Gulgowski",
},
{
id: "9",
name: "Ellen Veum",
},
{
id: "10",
name: "Lorena Rice",
},
{
id: "11",
name: "Carlton Zieme",
},
{
id: "12",
name: "Jessie Dickinson",
},
{
id: "13",
name: "Julian Gulgowski",
},
{
id: "14",
name: "Ellen Veum",
},
{
id: "15",
name: "Lorena Rice",
},
];
`
Open code in Expo Snack > https://snack.expo.dev/#stefanosalexandrou/d6a238
I create a list with Map method. Every item in screen is selectable. When Item selected then background of selected item changed. Also the code get the Y position of item in window screen.
What I want:
I want when click item, the selected item from start Y position move/scroll to the middle of ScrollView.
Like this> https://www.youtube.com/shorts/XqX0x9OzN8M,
https://www.youtube.com/shorts/an2_Dih-D8g

React Native: How to implement 2 columns of swipping cards?

I am trying to implement a scrollable list of cards in 2 columns. The cards should be swipe-able left or right out of the screen to be removed.
Basically, it should be like how the Chrome app is showing the list of tabs currently, which can be swiped away to be closed. See example image here.
I am able to implement the list of cards in 2 columns using FlatList. However, I have trouble making the cards swipe-able. I tried react-tinder-card but it cannot restrict swiping up and down and hence the list becomes not scrollable. react-native-deck-swiper also does not work well with list.
Any help is appreciated. Thank you!
I am going to implement a component that satisfies the following requirements:
Create a two column FlatList whose items are your cards.
Implement a gesture handling that recognizes swipeLeft and swipeRight actions which will remove the card that was swiped.
The swipe actions should be animated, meaning we have some kind of drag of the screen behavior.
I will use the basic react-native FlatList with numColumns={2} and react-native-swipe-list-view to handle swipeLeftand swipeRight actions as well as the desired animations.
I will implement a fire and forget action, thus after removing an item, it is gone forever. We will implement a restore mechanism later if we want to be able to restore removed items.
My initial implementation works as follows:
Create a FlatList with numColumns={2} and some additional dummy styling to add some margins.
Create state using useState which holds an array of objects that represent our cards.
Implement a function that removes an item from the state provided an id.
Wrap the item to be rendered in a SwipeRow.
Pass the removeItem function to the swipeGestureEnded prop.
import React, { useState } from "react"
import { FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"
const data = [
{
id: "0",
title: "Title 1",
},
{
id: "1",
title: "Title 2",
},
{
id: "2",
title: "Title 3",
},
{
id: "3",
title: "Title 4",
},
{
id: "4",
title: "Title 5",
},
{
id: "5",
title: "Title 6",
},
{
id: "6",
title: "Title 7",
},
{
id: "7",
title: "Title 8",
},
]
export function Test() {
const [cards, setCards] = useState(data)
const [removed, setRemoved] = useState([])
function removeItem(id) {
let previous = [...cards]
let itemToRemove = previous.find((x) => x.id === id)
setCards(previous.filter((c) => c.id !== id))
setRemoved([...removed, itemToRemove])
}
return (
<SafeAreaView style={{ margin: 20 }}>
<FlatList
data={cards}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ index, item }) => (
<SwipeRow swipeGestureEnded={() => removeItem(item.id)}>
<View />
<View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
<Text>{item.title}</Text>
</View>
</SwipeRow>
)}
/>
</SafeAreaView>
)
}
Notice that we need some kind of property in our objects in order to determine which one we want to remove. I have used a basic id property here, which is quite common using FlatList. If you are retrieving your data from an API which does not provide the same id, then we could just do some preprocessing (normalization) first and add the id prop ourselves.
The initial view looks as follows.
Swiping, let's say the item with 'Title 6' to the right or to the left removes it.
It might be desired to implement the following feature as well.
If the item is in the first column, then only swiping to the left will remove the item.
If the item is in the second column, then only swiping to the right will remove the item.
This is easily implemented using the index param which is passed to the renderItem function and the vx prop of the gestureState passed to the swipeGestureEnded function.
Here is fully working implementation.
import React, { useState } from "react"
import { FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"
const data = [
{
id: "0",
title: "Title 1",
},
{
id: "1",
title: "Title 2",
},
{
id: "2",
title: "Title 3",
},
{
id: "3",
title: "Title 4",
},
{
id: "4",
title: "Title 5",
},
{
id: "5",
title: "Title 6",
},
{
id: "6",
title: "Title 7",
},
{
id: "7",
title: "Title 8",
},
]
export function Test() {
const [cards, setCards] = useState(data)
const [removed, setRemoved] = useState([])
function removeItem(id) {
let previous = [...cards]
let itemToRemove = previous.find((x) => x.id === id)
setCards(previous.filter((c) => c.id !== id))
setRemoved([...removed, itemToRemove])
}
return (
<SafeAreaView style={{ margin: 20 }}>
<FlatList
data={cards}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ index, item }) => (
<SwipeRow
swipeGestureEnded={(key, event) => {
if (event.gestureState.vx < 0) {
if (index % 2 === 0) {
removeItem(item.id)
}
} else if (event.gestureState.vx >= 0) {
if (index % 2 === 1) {
removeItem(item.id)
}
}
}}
disableLeftSwipe={index % 2 === 1}
disableRightSwipe={index % 2 === 0}>
<View />
<View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
<Text>{item.title}</Text>
</View>
</SwipeRow>
)}
/>
</SafeAreaView>
)
}
Since the index is zero based in a FlatList, an item is in the second column if and only if index % 2 === 1 (e.g. an item with index 3 is always in the second column and thus not divisible by 2), on the other hand an item is in the first column if and only if index % 2 === 0 that is index is divisible by 2.
There are several callback function props in the SwipeRowComponent that should be fired in certain situations. However, most of them did not work in my setup and I still have no clue why. I got it to work by using the event.gestureState.vx property which is negative if we swipe to the left and positive (including zero) if we swipe to the right.
It might be desired to implement an undo button as it is quite common in this kind of functionalities. This can be done as follows:
Implement a second state which represents a Queue that holds lastly removed items. The undo button then just pops the lastly removed item.
Here is a fully working implementation with a dummy undo button that achieves exactly that.
import React, { useState } from "react"
import { Button, FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"
const data = [
{
id: "0",
title: "Title 1",
},
{
id: "1",
title: "Title 2",
},
{
id: "2",
title: "Title 3",
},
{
id: "3",
title: "Title 4",
},
{
id: "4",
title: "Title 5",
},
{
id: "5",
title: "Title 6",
},
{
id: "6",
title: "Title 7",
},
{
id: "7",
title: "Title 8",
},
]
export function Test() {
const [cards, setCards] = useState(data)
const [removed, setRemoved] = useState([])
function removeItem(id) {
let previous = [...cards]
let itemToRemove = previous.find((x) => x.id === id)
setCards(previous.filter((c) => c.id !== id))
setRemoved([...removed, itemToRemove])
}
function undoRemove() {
if (removed && removed.length > 0) {
let itemToUndo = removed[removed.length - 1]
setCards([...cards, itemToUndo])
setRemoved(removed.filter((c) => c.id !== itemToUndo.id))
}
}
return (
<SafeAreaView style={{ margin: 20 }}>
<FlatList
data={cards}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ index, item }) => (
<SwipeRow
swipeGestureEnded={(key, event) => {
if (event.gestureState.vx < 0) {
if (index % 2 === 0) {
removeItem(item.id)
}
} else if (event.gestureState.vx >= 0) {
if (index % 2 === 1) {
removeItem(item.id)
}
}
}}
disableLeftSwipe={index % 2 === 1}
disableRightSwipe={index % 2 === 0}>
<View />
<View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
<Text>{item.title}</Text>
</View>
</SwipeRow>
)}
/>
<Button onPress={undoRemove} title="Undo" />
</SafeAreaView>
)
}
Notice that my undo button just appends the removed item to the end of the list. If you want to keep the initial index, then you need to save the old index and push the item to the correct position.
Here is workin snack of my last implementation.

Display search output using json data in react native

I am at a very primitive stage of learning react-native. And I am trying to solve a simple problem, which may sound silly, but I really want to know the answer.
I have a json file
data.js
export const PRODUCT_DATA = [
{
name: 'abc',
price: 90,
weight: '1 kg',
currency: 'INR',
liked: true,
image: require('../assets/images/carrots/Rectangle238.png')
},
{
name: 'bce',
price: 10,
weight: '1 kg',
currency: 'USD',
liked: false,
image: require('../assets/images/mango/Rectangle234.png')
},
{
AllCategoriesComponent: [
{
icon: "home-outline",
name: "Household",
shape: true,
},
{
icon: "basket-outline",
name: "Grocery",
shape: false,
},
{
icon: "ios-podium",
name: "Milk",
shape: true,
},
{
icon: "ios-rose",
name: "chilled",
shape: false,
},
{
icon: "hardware-chip",
name: "Drinks",
shape: true,
},
{
icon: "cloud",
name: "Pharmacy",
shape: true,
},
{
icon: "fast-food",
name: "Frozen Food",
shape: true,
},
{
icon: "football",
name: "Vegetable",
shape: true,
},
{
icon: "bulb",
name: "Meat",
shape: true,
},
{
icon: "football",
name: "Vegetable",
shape: true,
},
{
icon: "bulb",
name: "Meat",
shape: true,
},
]
},
];
ANd below is screen file
screen.js
import { SearchBar } from 'react-native-elements';
import { Text, View, TextInput } from 'react-native';
import React from 'react';
import { PRODUCT_DATA } from "./data";
export default class App extends React.Component {
constructor() {
super();
this.state = {
search: '',
}
}
updateSearch = (search) => {
this.setState({ search: search });
};
render() {
const { search } = this.state;
return (
<View>
<SearchBar onChangeText={this.updateSearch} value={search} />
{PRODUCT_DATA[2].AllCategoriesComponent.map((item, index) => {
if (item.name === this.state.search) {
return (
<View style={{ backgroundColor: "white" }}>
<Text>{search}</Text>
</View>
);
} else {
return (<Text></Text>);
}
})}
<Text>{this.state.search}</Text>
</View>
);
}
}
As you can see this is not a good solution. I am able to print the output only if I type full name in the SearchBar. Also it seems all the item.name are already on the screen, which comes up when value of search bar matches it. I want to start showing the output as soon as something is typed on the SearchBar
This might help please look into it
import { FlatList, Text, View, TextInput } from "react-native";
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
data: [],
};
this.arrayholder = [];
}
componentDidMount() {
const data = PRODUCT_DATA[2].AllCategoriesComponent.map((item, index) => {
return item;
});
this.setState({ data }, () => {
this.arrayholder = data;
});
}
searchData(text) {
const newData = this.arrayholder.filter((item) => {
const itemData = item.name.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
text: text,
});
}
render() {
return (
<View style={styles.MainContainer}>
<TextInput
onChangeText={(text) => this.searchData(text)}
value={this.state.text}
placeholder="Search Here"
/>
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text style={styles.row}>{item.name}</Text>}
/>
</View>
);
}
}

Is it possible to set fixed widths on tables in material-table?

I'd like to be able to have fixed widths in my react table. I'm using this library material-table
import React from "react";
import MaterialTable from "material-table";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<MaterialTable
columns={[
{ title: "Name", field: "name" }, // 100px
{ title: "Surname", field: "surname" }, // set to 100px
{ title: "Birth Year", field: "birthYear", type: "numeric" }, // fill rest of row space
]}
data={[
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Zerya Betül",
surname: "Baran",
birthYear: 2017,
birthCity: 34
}
]}
title="Basic"
options={{
toolbar: false,
paging: false
}}
/>
);
}
Looks like you can use the headerStyle prop to set the widths.
import React from "react";
import MaterialTable from "material-table";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<MaterialTable
columns={[
{ title: "Name", field: "name", headerStyle: {width: "100px"} },
{ title: "Surname", field: "surname", headerStyle: {width: "100px"} },
{ title: "Birth Year", field: "birthYear", type: "numeric" },
]}
data={[
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Zerya Betül",
surname: "Baran",
birthYear: 2017,
birthCity: 34
}
]}
title="Basic"
options={{
toolbar: false,
paging: false
}}
/>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

React native component does not react to mobx observable data change

so I started to build a new app with react native and mobx.
I have a flat list component that gets his state data from the mobx store list. and when i'm adding new item to the mobx list, it won't re render the flat list view.
here is my code:
List Component:
#inject('TravelStore')
#observer
class TripsList extends Component {
constructor(props) {
super(props);
this.state = {
trips_list: props.TravelStore.trips_list
}
};
// set the navigation bar options
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title: 'Your Trips',
headerRight: (
<Button transparent primary onPress={ params.addNewTrip }>
<Icon name='ios-add' />
</Button>
)
};
};
// connect between component functions to header
componentDidMount() {
this.props.navigation.setParams({
addNewTrip: this._addNewTrip.bind(this),
});
}
_addNewTrip() {
this.props.TravelStore.addNewTrip('bla')
}
_renderListItem({ item }) {
return (
<TripsListItem details={item} navigation={this.props.navigation}/>
);
};
render() {
return (
<Container>
<FlatList
data = {this.state.trips_list}
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
/>
</Container>
);
};
}
mobx store:
class ObservableTravelListStore {
#observable trips_list = [
{
name: 'to denver',
trip_cost: 400,
buying_list: [
{ name: 'pizza', price: 10 },
{ name: 'burger', price: 40 },
{ name: 'ipad', price: 44 },
{ name: 'bus', price: 45 },
]
},
{
name: 'to yafo',
trip_cost: 30,
buying_list: [
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
},
{
name: 'to tel aviv',
trip_cost: 50,
buying_list: [
{ name: 'na na na', price: 10 },
{ name: 'no no no', price: 40 },
]
},
]
#action addNewTrip (trip_data) {
this.trips_list.push({
name: 'newTrip',
trip_cost: 6060,
buying_list: [
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
})
console.log(this.trips_list[3])
}
}
const TravelStore = new ObservableTravelListStore()
export default TravelStore
any idea why the TripsList component won't rerender when addNewTrip function is called?
the problem is that you are not listening to the real observable but to a copy of it, you save in state in the constructor.
<FlatList
data = {this.state.trips_list}//change this
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
/>
<FlatList
data = {this.props.TravelStore.trips_list}//change to this
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
/>
the render function is like autobind of mobx and react to changes in the observable if it's a render function of an observer
if you want to react to inner changes in the items of the list,
you should add an observable scheme and this should do the trick,
something like this:
class TripModel {
#observable name = ''
#observable trip_cost = 0
#observable buying_list = []
constructor(name, cost, buy_list){
this.name = name
this.trip_cost = cost
this.buying_list = buy_list
}
/* class functions*/
}
class ObservableTravelListStore {
#observable trips_list = [
new Trip(
'to denver',
400,
[
{ name: 'pizza', price: 10 },
{ name: 'burger', price: 40 },
{ name: 'ipad', price: 44 },
{ name: 'bus', price: 45 },
]
),
new Trip(
'to yafo',
30,
[
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
),
new Trip(
'to tel aviv',
50,
[
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
)
]
#action addNewTrip (trip_data) {
this.trips_list.push(new Trip(
'newTrip',
6060,
[
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
))
}
}
const TravelStore = new ObservableTravelListStore()
export default TravelStore
this is just better planning for reactive apps, so on change to inner content of the items in the list you will react to this change
hope that helps
Its an old post, but I also got stuck with something similar recently. Adding extraData in Flatlist prop list helped me.
<FlatList
data = {this.props.TravelStore.trips_list}
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
extraData={this.props.TravelStore.trips_list.length} // list re-renders whenever the array length changes
/>
And as #Omri pointed out, you shouldn't be storing the observable in the Component state but make changes to it directly.