react-select Render custom component as SingleValue - react-select

I need to display an icon before the selected value using react-select and typescript.
This is what I tried so far:
SingleValue: React.SFC<SingleValueProps<OptionType>> = ({ children, ...props }) => (
<components.SingleValue {...props}>
<i className={`fal fa-${this.props.icon} has-text-grey-light`} /> {children}
</components.SingleValue>
)
The main issue is with type definitions that expects that children passed to components.SingleValue must be a string.

You don´t have to use the standard components. You can easlily create a custom one but still keep the styles it needs.
The only requirement you need is emotion to get the styles the SingleValue component uses.
/**
* This Example uses the `FontAwesome` React library.
**/
const options = [
{ label: "Value A", value: "a", icon: faCoffee },
{ label: "Value B", value: "b", icon: faCar },
{ label: "Value C", value: "c", icon: faMobile },
{ label: "Value D", value: "d", icon: faCircle },
{ label: "Value E", value: "e", icon: faSquare }
];
const SingleValue = ({
cx,
getStyles,
selectProps,
data,
isDisabled,
className,
...props
}) => {
console.log(props);
return (
<div
className={cx(
emotionCss(getStyles("singleValue", props)),
{
"single-value": true,
"single-value--is-disabled": isDisabled
},
className
)}
>
<FontAwesomeIcon icon={data.icon} style={{ marginRight: 10 }} />
<div>{selectProps.getOptionLabel(data)}</div>
</div>
);
};
export default class MySelect extends Component {
render() {
return (
<Select
options={options}
components={{ SingleValue }}
styles={{
singleValue: (provided, state) => ({
...provided,
display: "flex", // To keep icon and label aligned
alignItems: "center"
})
}}
/>
);
}
}
Working example

Related

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.

react-hook-form: how to get the select value

I am using react-hook-form with react-select. The below is the code
import CreatableSelect from "react-select/creatable";
const scriptOptions = [
{ value: "imd", label: "Immediate" },
{ value: "sch", label: "Scheduled" },
];
<Controller
name="resetType"
control={control}
render={({ field }) => (
<CreatableSelect
{...field}
options={scriptOptions}
/>
)}
/>
the value of resetType looks like
resetType: {
label: "Immediate"
value: "imd"
}
instead I want
resetType: "imd"
How to get this done

What config and options do I need for react-native-highcharts to make a highstock OHLC graph?

I've been going through HighStock API to try and find which config and options I need to pass to the ChartView component in react-native-highcharts to draw my graph. I'm having a hard time finding what combination of config and options will get my desired result, things like series, dataGrouping, etc... . My data is a 2 dimensional array with 4 values for OHLC. My desired result is the photo at the top of this stackoverflow.
Here is my code so far.
class OHLC extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: "OHLC",
headerLeft: (
<TouchableOpacity
style={NavStyles.headerButton}
onPress={() => navigation.openDrawer()}>
<Icon name="bars" size={20} />
</TouchableOpacity>
),
})
render() {
var Highcharts='Highcharts';
var conf={
title: {
text: 'Stock Name'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Price'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
// tooltip: {
// formatter: function () {
// return '<b>' + this.series.name + '</b><br/>' +
// Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
// Highcharts.numberFormat(this.y, 2);
// }
// },
legend: {
enabled: false
},
// exporting: {
// enabled: false
// },
series: [{
type: 'ohlc',
name: 'AAPL Stock Price',
data: (function () {
let arrays = aExtractFromJson(data,'data', null,null);
arrays = ohlcExtractor(arrays);
return arrays;
// look at toFixed method for number to limit decimal point
}()),
dataGrouping: {
units: [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]]
}
}]
};
const options = {
global: {
useUTC: false
},
lang: {
decimalPoint: ',',
thousandsSep: '.'
}
};
return (
<View>
<ChartView style={{height:300}} config={conf} options={options} stock={true} ></ChartView>
//To see if anything gets rendered.
<Text>HELLO DAVID!</Text>
</View>
);
}
}
After further research, I was able to find the config and options needed to create an OHLC Graph using the ChartView component in react-native-highcharts. I encountered some issues with rendering a blank screen so I added javaScriptEnabled={true} domStorageEnabled={true} originWhitelist={['']} to my ChartView.
In the config the essentials:
series with type: 'ohlc' and data: [[1,2,3,4],[2,3,4,5]] inside.
In options, no arguments are required, I left it as options='' in the ChartView.
Don't forget to add stock={true} in ChartView
My code, basic example:
import React, {Component} from 'react';
import {View} from 'react-native';
import ChartView from 'react-native-highcharts';
class OHLC extends React.Component {
constructor(props) {
super(props);
}
render() {
var Highcharts='Highcharts';
var conf={
chart: {
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
type: 'ohlc',
name: 'Random data',
/*Open, high,low,close values in a two dimensional array(two days)*/
data: [[1,2,3,4],[2,3,4,5]],
}]
};
return (
<View style={{borderRadius: 4, marginTop: 30,}}>
<ChartView style={{height:500}} config={conf} javaScriptEnabled={true} domStorageEnabled={true} originWhitelist={['']} stock={true} options=''></ChartView>
</View>
);
}
}

Set Selected items in react native multi select

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.

Modify header according to one parameter

I was trying to modify my custom header according to one parameter that is in the state from the same component. But I see that it does not work. I can do the same inside the render and it obviously does but how can I do it in the header?
In this case for instance, I would like to change the button for another if itemNumber > 0
...
static navigationOptions = ({ navigation }) => {
return{
title: "Edit Group"+' ',
headerStyle: {
backgroundColor: '#2ca089',
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
},
headerTintColor: '#fff',
headerRight: (
<Button hasText transparent onPress={() =>
Alert.alert(
"Delete Group",
"Are you sure you want to delete this group? It is a non-reversible action!",
[
{text: 'Yes', onPress: () => console.log('Yes Pressed')},
{text: 'No', onPress: () => console.log('No Pressed'), style: 'cancel'},
],
)
}>
<Text>Delete Group</Text>
</Button>
),
};
}
constructor(props) {
super(props)
this.state = {
dataSource : [],
text : '',
itemNumber : 0,
}
}
...
I understand that because it is a static component it will not be modified dynamically but I do not see how can I do it in another fashion.
Thanks,
I can not see where the answer from TNC implement the callback function inside the headerRight in order to re-update the navigation header which I think it is the problem.
My solution is the following:
The variable which you want to observe is itemNumber, make sure is in the constructor ✅
constructor(props) {
super(props)
this.state = {
dataSource : [],
text : '',
itemNumber : 0,
selectedItems: []
}
}
Then, inside the function which you trigger the event which requires the update for the header add the following code:
triggerFunction = parameters => {
//...
let newValue = 1 //Implement your logic
this.setState(({itemNumber}) => {
this.props.navigation.setParams({itemNumber: newValue})
return {itemNumber: newValue }
});
//...
};
To conclude, on the navigationOption add the following code:
static navigationOptions = ({ navigation }) => {
return{
headerRight:
navigation.getParam('itemNumber', 0) > 0
?
<Button hasText transparent onPress={() =>
Alert.alert(
"Delete User",
"Are you sure you want to delete this user/users? It is a non-reversible action!",
[
{text: 'Yes', onPress: () => console.log('Yes Pressed')},
{text: 'No', onPress: () => console.log('No Pressed'), style: 'cancel'},
],
)
}>
<Text>Delete Users</Text>
</Button>
:
null
};
}
I have got the inspiration from this answer:
https://stackoverflow.com/a/51911736/5288983
I also attach you the documentation where you can understand better the approach:
https://reactnavigation.org/docs/en/headers.html#setting-the-header-title
You can pass multiple params to navigator as:
From Calling Container/Component
this.props.navigation.navigate('navigator-destination', {
title: 'Your Title',
label: 'Extra Params',
callback: callback // Even callback
});
In Called Container/Component
static navigationOptions = ({navigation}) => {
const {state} = navigation;
label = state.params.label;
return {
title: `${state.params.title}`,
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
};
};
To call callback:
_someMethod = () => {
const {navigation} = this.props;
navigation.state.params.callback(parameters);
navigation.goBack();
};