Connecting Pickers in React-Native - javascript

Is it possible to connect two pickers so that when there is a name in the first and another name in the second it automatically assigns them?
For example: user has first_name and last_name. When the user click on first picker and select first_name automatically change the value in second picker last_name.

Definitely yes, it's possible. But consider that the two names need something that relate each others. I will give you an example where state handle an array of names and a pickedName, which is the value both pickers are going to display. The two pickers are simply mapping different labels with same values.
state={
pickedName: {
firstName: '',
lastName: ''
},
names: [
{
firstName: 'Pinco',
lastName: 'Pallino'
},
{
firstName: 'Mario',
lastName: 'Rossi'
}
]
}
render() {
const { pickedName } = this.state;
return (
<View style={styles.container}>
<Picker
selectedValue={pickedName}
style={{ height: 50, width: 100 }}
onValueChange={(itemValue, itemIndex) =>
this.setState({ pickedName: itemValue })
}>
{this.state.names.map(name => (
<Picker.Item label={name.firstName} value={name} />
))}
</Picker>
<Picker
selectedValue={pickedName}
style={{ height: 50, width: 100 }}
onValueChange={(itemValue, itemIndex) =>
this.setState({ pickedName: itemValue })
}>
{this.state.names.map(name => (
<Picker.Item label={name.lastName} value={name} />
))}
</Picker>
</View>
);
}
Update:
Supposing you're fetching names from a DB during screen mounting. You can do it this way:
state = {
pickedName: {
firstName: '',
lastName: '',
},
names: [],
};
componentDidMount() {
this.getNamesFromDB();
}
getNamesFromDB = () => {
// Here you can call DB to get names list.
// So the following const wouldn't be hardcoded as I did
const nameList = [
{
firstName: 'Pinco',
lastName: 'Pallino',
},
{
firstName: 'Mario',
lastName: 'Rossi',
},
];
// set the component state with the list you've received
// (by default pickers show the first element of the list, so you don't need to specify pickedName,
// but if you want to select another element you can do it this way)
this.setState({
names: nameList,
// pickedName: nameList[1] // <-- to select the second element by default
});
}
render() {
const { pickedName } = this.state;
return (
<View style={styles.container}>
<Picker
selectedValue={pickedName}
style={{ height: 50, width: 200 }}
mode={'dropdown'}
onValueChange={(itemValue, itemIndex) =>
this.setState({ pickedName: itemValue })
}>
{this.state.names.map(name => (
<Picker.Item label={name.firstName} value={name} />
))}
</Picker>
<Picker
selectedValue={pickedName}
style={{ height: 50, width: 200 }}
mode={'dropdown'}
onValueChange={(itemValue, itemIndex) =>
this.setState({ pickedName: itemValue })
}>
{this.state.names.map(name => (
<Picker.Item label={name.lastName} value={name} />
))}
</Picker>
</View>
);
}
I've also created a snack if you wanna take a look. Hope I've been clear enough, I'm here for every clarifications or suggestions.

Related

can't use recursive map in react component

i stumbled into an issue i cant solve, i have an object 'customerDraft' which has nested object in it. i want to render every field plus the fields which are inside of 'customerDraft.metadata'.
my component looks like this:
const CustomerDetailEditModal = (props) => {
const {
open,
setOpen,
customerDraft,
customerProfileDraft,
setDraftCustomer,
setDraftProfile,
onUpdate
} = props;
const classes = useStyles();
const dispatch = useDispatch();
const [isPasswordHidden, setIsPasswordHidden] = useState(true);
// const [attributes, setAttributes] = useState({});
const projectId = useSelector(({ project }) => project.currentProject._id);
const generatedPassword = useSelector(({ customer }) => customer.password);
const isCurrentProjectCapstone = projectId === '4387564328756435';
const onModalCancel = () => {
setOpen(false);
if (isCurrentProjectCapstone) {
dispatch(removeItemFromCustomerDraftAction('password'));
}
};
const generatePassword = () => {
dispatch(getGeneratedPassword());
};
useEffect(() => {
if (!generatedPassword) return;
setDraftCustomer({
...customerDraft,
password: generatedPassword
});
// eslint-disable-next-line
}, [generatedPassword]);
console.log(customerDraft);
return (
<div>
<Modal
bodyStyle={{
fontSize: '12px',
height: 500,
margin: '0 auto'
}}
centered
footer={
<div
style={{
display: 'flex',
justifyContent: 'flex-end'
}}>
<CButton
htmlType="submit"
onClick={(e) => {
setOpen(false);
e.preventDefault();
}}
size="large"
type="secondary">
Cancel
</CButton>
<CButton
htmlType="submit"
onClick={onUpdate}
size="large"
type="primary"
// disabled={!isSaveEnabled}
>
Save
</CButton>
</div>
}
onCancel={onModalCancel}
title={
<span
style={{
fontSize: '24px',
fontWeight: 700,
lineHeight: '24px'
}}>
Edit User
</span>
}
visible={open}
width={customerProfileDraft ? 770 : 385}>
<form className={classes.form} id="customer-edit-form">
<div className={classes.wrapperDiv}>
{Object.entries(customerDraft).map((item, i) => {
if (customerDraft.fullName) {
if (restrictedData.includes(item[0] || item[0].toLowerCase().includes('id'))) {
return false;
}
}
if (restrictedData.includes(item[0]) || item[0].toLowerCase().includes('id')) {
return false;
}
return (
<CStandardInput
key={i}
allowClear
defaultValue={item[1]}
disableUnderline
formclasses={{ root: classes.root }}
htmlFor={`standard-customer-edit-${item[0]}`}
id={`standard-customer-edit-${item[0]}`}
label={item[0]}
onChange={(event) => {
setDraftCustomer({
...customerDraft,
fullName: event.target.value
});
setDraftProfile({
...customerProfileDraft,
fullName: event.target.value
});
}}
size="large"
/>
);
})}
{isCurrentProjectCapstone && (
<div className={classes.passwordWrapper}>
<CStandardInput
adornment={
<>
<button
className={classes.buttonSvg}
onClick={() => {
navigator.clipboard.writeText(customerDraft.password || '');
}}
style={{
marginRight: '5px'
}}
type="button">
<img alt="copy password" src={copyIcon} />
</button>
<button
className={classes.buttonSvg}
onClick={() => setIsPasswordHidden(!isPasswordHidden)}
type="button">
<img
alt="toggle password visibility"
src={isPasswordHidden ? crossedEyeIcon : eyeIcon}
/>
</button>
</>
}
disableUnderline
formclasses={{ root: classes.root }}
htmlFor="standard-input-user-password"
id="standard-input-user-password"
label="Password"
onChange={(e) => setDraftCustomer({ ...customerDraft, password: e.target.value })}
size="large"
type={isPasswordHidden ? 'password' : 'text'}
value={customerDraft.password || ''}
width="true"
/>
<CButton
onClick={generatePassword}
type="primary"
xstyle={{
borderRadius: '12px',
margin: '16px 0px 0px 16px'
}}>
Generate
</CButton>
</div>
)}
</div>
</form>
</Modal>
</div>
);
};
export default CustomerDetailEditModal;
notice how metdata field is rendered? i want to use recursion to output every field which metadata contains,
i know recursion but what i cant seem to figure out is where should this component call itself to do it.
any help with explanation so that i can understand the answer would be much appreciated!
this is the object im iterating on:
const customerData = {
createdAt: "2022-10-28T08:42:08.015Z",
email: "company#gmail.com",
firstName: "$$$$$$$",
fullName: "$$$$$$",
idNumber: "2813921321",
isEmailVerified: true,
isPhoneVerified: true,
lastName: "$$$$$",
metadata: {
birthDate: "2000-08-19 00:00:00.000",
gender: "Male",,
region: "",
status: "Adult",
statusExtra: "Student",
},
phone: "######",
project: "hlkjhkljhkjhk",
updatedAt: "2022-11-01T10:26:32.677Z",
username: null,
_id: "hlkjhlkjhlkjhlkjhlkjh",
};
see metadata? currently im outputting only the fields of the main(parent) object, but i also want to output the data which is contained in the 'metadata' key using recursion.
A solution to this could be to check if the key item[0] is "metadata". Then you could do the same as you did with the customerDraft object. Get the entries an map over them.
Note that I destructured the array you get from the .entries to make it more explicit what the variables are.
if (item[0] === "metadata") {
const inputs = Object.entries(item[1]).map(([metaKey, metaValue]) => (
<CStandardInput
key={metaKey}
allowClear
defaultValue={metaValue}
disableUnderline
formclasses={{ root: classes.root }}
htmlFor={`standard-customer-edit-${metaKey}`}
id={`standard-customer-edit-${metaKey}`}
label={metaKey}
onChange={(event) => {
setDraftCustomer({
...customerDraft,
fullName: event.target.value,
});
setDraftProfile({
...customerProfileDraft,
fullName: event.target.value,
});
}}
size="large"
/>
));
return <>{inputs}</>;
}
return (
<CStandardInput
...
EDIT:
To support the nested data with recursion, I've created a function with returns an input for every key, value pair in the data.
You can add your extra if statements as desired
const renderInputs = (data) => {
const inputs = Object.entries(data).map(([key, value]) => {
if (
typeof value === "object" &&
!Array.isArray(value) &&
value !== null
) {
return renderInputs(value);
}
return (
<CStandardInput
key={key}
allowClear
defaultValue={value}
disableUnderline
formclasses={{ root: classes.root }}
htmlFor={`standard-customer-edit-${key}`}
id={`standard-customer-edit-${key}`}
label={key}
onChange={(event) => {
setDraftCustomer({
...customerDraft,
fullName: event.target.value,
});
setDraftProfile({
...customerProfileDraft,
fullName: event.target.value,
});
}}
size="large"
/>
);
});
return inputs;
};
return <>{renderInputs(customerData)}</>;
Hope this helps you with your project!

React-Native: Expo stops when I add Picker item

I'm new to react-native, I'm trying to use Picker item to selct from a dropdown, I'm using react hooks to set Picker items to be selectable, however Expo stops without warning when I add the code:
const HomeScreen3 = observer(() =>
{
const [ options, setOptions ] = useState([ { id:1, title:'Titulo 1' }, { id:2, title:'Titulo 2' } ]);
const [ selectedOption, setSelectedOption ] = useState({ id:1, title:'Titulo 1' });
useEffect(() => {
console.log('component mounted');
}, []);
return (
<View style={{flex: 1, backgroundColor: '#fff', padding:10, position:'relative' }}>
<ScrollView showsHorizontalScrollIndicator={false}>
<Picker
itemStyle={{ backgroundColor: "white", color: "black", borderColor:'rgba(0,0,0,0.2)', fontSize:16, }}
mode="dropdown"
selectedValue={selectedOption}
onValueChange={(value) => { setSelectedOption( value)}}>
{options.map((item, index) => {
return (<Picker.Item label={item} value={item} key={index}/>)
})}
</Picker>
</ScrollView>
</View>
export default HomeScreen3;
for expo use this works for me even is there a warn but works!
import { Picker } from 'react-native'
// state
const [picked, setPicked] = useState(1.15);
<Picker
selectedValue={picked}
style={{ height: 50, width: 100 }}
onValueChange={(itemValue, itemIndex) =>
setPicked(itemValue)
}>
<Picker.Item label="Canda 5%" value={1.05} />
<Picker.Item label="Japan 8%" value={1.08} />
<Picker.Item label="USA 10%" value={1.10} />
<Picker.Item label="Egypt 14%" value={1.14} />
<Picker.Item label="Saudi Arabia 15%" value={1.15} />
<Picker.Item label="China 16%" value={1.16} />
<Picker.Item label="Algeria 17%" value={1.17} />
<Picker.Item label="18%" value={1.18} />
<Picker.Item label="German 19%" value={1.19} />
<Picker.Item label="German 19%" value={1.20} />
</Picker>
First of all, in Expo SDK 38 the import { Picker } from 'react-native'; is no longer working as expected.
Make sure you use import { Picker } from '#react-native-community/picker';
Here is the documentation for it: https://github.com/react-native-community/react-native-picker.
Second, the real problem I see is an issue with your code is that for the Picker.Item, here:
{options.map((item, index) => {
return (<Picker.Item label={item} value={item} key={index}/>)
})}
you are sending the complete objects in the label and item props. However, looking at the actual documentation, label accepts a string and value accepts string or integer - https://reactnative.dev/docs/picker-item
That means your Picker code should look like this:
<Picker
itemStyle={{backgroundColor: "white", color: "black", borderColor:'rgba(0,0,0,0.2)', fontSize:16}}
mode="dropdown"
selectedValue={selectedOption}
onValueChange={(value) => {setSelectedOption(value)}}>
{options.map((item, index) => {
return (<Picker.Item label={item.id} value={item.title} key={index}/>)
})}
</Picker>
and the selectedOption should only store the id of the item like this:
const [ selectedOption, setSelectedOption ] = useState(1);

add data from firestore query to array object and map them into Picker react native

my constructor has:
this.ActiveDelegation = {
cityArr: [],
name: '',
expiration: '',
}
this is how i get the data from firestore into the object and i assume it add the name inside the cittyArr[] i've created
getDelegationList = (querySnapshot) => {
console.log("snapshot")
querySnapshot.forEach((res) => {
const {name, expiration} = res.data();
console.log('res is =>', res.data())
this.ActiveDelegation.cityArr.push({
key: res.id,
res,
name,
expiration,
});
//return <Picker.Item value={name} label={name} />
});
console.log('test2: ', this.ActiveDelegation.cityArr)
//console.log('test12312: ',this.ActiveDelegation.cityArr.city)
delegList = () => {
return (this.ActiveDelegation.cityArr.map((c, i) => {
return (
<Picker.Item label = {c.name} value = {c.name} key = {i}/>
)
}
))
}
}
this is inside render:
<Item picker>
<Picker
mode="dropdown"
style={{ width: 20 }}
placeholder="choose destination"
placeholderStyle={{ color: "#bfc6ea" }}
placeholderIconColor="#007aff"
// selectedValue={this.state.Destination}
// onValueChange={this.onValueChangeG.bind(this)}
selectedValue={this.state.selectedDest}
onValueChange={ (value) => ( this.setState({selectedDest: value}) ) } >
{this.delegList}
{/*{cityItems}*/}
<Picker.Item label="select" value="" />
</Picker>
</Item>
but nothing happen inside my picker..
can anyone help?
i found a solution. just used the map inside the render itself under the picker scope:
<Item picker>
<Picker
mode="dropdown"
style={{ width: 20 }}
placeholder="choose destination"
placeholderStyle={{ color: "#bfc6ea" }}
placeholderIconColor="#007aff"
selectedValue={this.state.Destination}
onValueChange={ (value) => ( this.setState({Destination: value}) ) } >
{/* onValueChange={this.onValueChangeG.bind(this)} >*/}
{/*<Picker.Item label="choose" value="" />*/}
{
this.state.destinationArr.map( (city, i) => {
return (
<Picker.Item label={city.name} value={city.name} key={i} />
)
})
}
</Picker>
</Item>
fixed*

map is not a function and picker problem in react native

I have problem with map my array about curriences.
so as u see there is a curriences array with strings. Im using Picker to choose some between them and I have function what is included to onValueChange prop in Picker, and then comes my problem with selecting an item from picker.
Firstly I can choose a whatever item from picker, but when I want to choose again I have just this choosed item before in the list:
then i choosed for example EUR. When I want to choose a item again I have just this:
Also when I change first picker item - it changes also in the second picker... don't know why.
also adding whole code here:
import React, {Component} from 'react';
import {View, Text, TextInput, Picker} from 'react-native';
class CurrencyCashScreen extends React.Component {
state = {
currencies: ['USD', 'AUD', 'SGD', 'PHP', 'EUR'],
base: 'PLN',
amount: '',
convertTo: 'EUR',
result: '',
date: '',
};
handleSelect = (itemValue, itemIndex) => {
this.setState(
{
...this.state,
currencies: [itemValue],
result: null,
},
this.calculate,
);
};
handleInput = text => {
this.setState(
{
...this.state,
amount: text,
result: null,
date: null,
},
this.calculate,
);
};
calculate = () => {
const amount = this.state.amount;
if (amount === isNaN) {
return;
} else {
fetch(`https://api.exchangeratesapi.io/latest?base=${this.state.base}`)
.then(res => res.json())
.then(data => {
const date = data.date;
const result = (data.rates[this.state.convertTo] * amount).toFixed(4);
this.setState({
...this.state,
result,
date,
});
});
}
};
handleSwap = e => {
const base = this.state.base;
const convertTo = this.state.convertTo;
e.preventDefault();
this.setState(
{
...this.state,
convertTo: base,
base: convertTo,
result: null,
},
this.calculate,
);
};
render() {
const {currencies, base, amount, convertTo, result} = this.state;
return (
<View>
<Text>
{amount} {base} is equevalent to
</Text>
<Text>
{amount === '' ? '0' : result === null ? 'Calculating...' : result}{' '}
{convertTo}
</Text>
<View>
<View>
<View>
<TextInput
keyboardType="numeric"
value={amount}
onChangeText={this.handleInput}
/>
<Picker
selectedValue={base}
value={base}
onValueChange={this.handleSelect}>
{currencies.map((currency, index) => (
<Picker.Item label={currency} value={currency}>
{currency}
</Picker.Item>
))}
</Picker>
</View>
<View>
<TextInput
editable={false}
value={
amount === ''
? '0'
: result === null
? 'Calculating...'
: result
}
/>
<Picker
selectedValue={convertTo}
value={convertTo}
onValueChange={this.handleSelect}>
{currencies.map(currency => (
<Picker.Item label={currency} value={currency}>
{currency}
</Picker.Item>
))}
</Picker>
</View>
</View>
<View>
<Text onClick={this.handleSwap}>CLICK ME</Text>
</View>
</View>
</View>
);
}
}
export default CurrencyCashScreen;
Please help.
See below example
you need two separate functions
import React, {Component} from 'react';
import {View, Text, TextInput, Picker} from 'react-native';
class CurrencyCashScreen extends Component {
state = {
currencies: ['USD', 'AUD', 'SGD', 'PHP', 'EUR'],
base: 'PLN',
convertTo: 'EUR',
amount: '',
result: '',
date: '',
};
handleSelectPicker1 = itemValue => {
this.setState(
{
base:itemValue
},
);
};
handleSelectPicker2 = itemValue => {
this.setState(
{
convertTo:itemValue
},
);
};
render() {
const {currencies, base, amount, convertTo, result,pickerValue1,pickerValue2} = this.state;
console.log('this.state',pickerValue1,pickerValue2)
return (
<View style={{flex:1}}>
<Picker
selectedValue={base}
//value={base}
onValueChange={this.handleSelectPicker1}>
{currencies.map((currency, index) => (
<Picker.Item label={currency} value={currency}/>
))}
</Picker>
<Picker
selectedValue={convertTo}
//value={convertTo}
onValueChange={
this.handleSelectPicker2
}>
{currencies.map(currency => (
<Picker.Item label={currency} value={currency}/>
))}
</Picker>
</View>
);
}
}
export default CurrencyCashScreen;
hope this will help you. Change this according to your requirement. Feel free for doubts.
Technically .map() requires an array to be executed on the variable.
I guess itemValue is not an array what you pass to handleSelect. That's why you have that error. I think if you modify the code to handle currencies as an array then it should work.
handleSelect = itemValue => {
this.setState(
{
...this.state,
currencies: [itemValue],
result: null,
},
this.calculate,
);
};
I hope that helps!
More than likely currencies is not an array. Here is what I recommended:
Check to make sure that you've properly destructured your state earlier in the render function like const { currencies } = state; Any typos here may cause the error.
Make sure currencies is set with a sane default. If currencies is undefined when your component first mounts, you will get this error.
Use console.log, or a debugger to view the value of currencies. If it is not an array (including undefined) when you call the currencies.map method, then you will get this error.

How to setState with Picker in React-native?

A feature in my project in province searching but now I'm using typing the name of province and now I want to use Picker in React-native to setState from value that user selected.How can I setState from value that user selected from Picker?
My searching function and constructor.
constructor(props) {
super(props);
this.state = {
currentCity: "Phuket",
searchText: ""
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
const { searchText } = this.state;
this.refs.modal3.close();
this.setState({ currentCity: searchText });
}
My Searching box and picker.
<TextInput
placeholder="City name"
ref={el => (this.element = el)}
style={styles.textInput}
value={this.state.searchText}
onChangeText={searchText => this.setState({ searchText })}
/>
<View>
<Button style={styles.btn} onPress={this.handleSubmit}>
<Text style={styles.submitText}>Submit</Text>
</Button>
</View>
{//Dropdown Picker
}
<Picker
selectedValue={this.state.language}
style={{ height: 50, width: 200 }}
onValueChange={(itemValue, itemIndex) =>
this.setState({ language: itemValue })
}
>
<Picker.Item label="Amnat Charoen" value="1906689" />
<Picker.Item label="Ang Thong" value="1621034" />
<Picker.Item label="Bangkok" value="1609350" />
from the code you provided in your question i think you are new to react-native. your code is wrong( you just copied & pasted code). this part selectedValue={this.state.language} in your Picker is wrong because there is no language in your state.
imagine you have a Picker which has list of cities. and you have a variable in state named selectedCity.
so your picker would be like this :
<Picker
selectedValue={this.state.selectedCity}
onValueChange={(itemValue, itemIndex) =>
this.setState({ selectedCity: itemValue })
}
>
<Picker.Item label="city1" value="1" />
<Picker.Item label="city2" value="2" />
</Picker>
this will make a Picker listing 2 cities( city1 - city2) and whenever user selects one of them this.setState() will be called and selectedCity in state will be initialized.
if you want to call setState in another method, just instead of
onValueChange={(itemValue, itemIndex) =>
this.setState({ selectedCity: itemValue })
}
use
onValueChange={(itemValue, itemIndex) =>
//call method here!
}

Categories

Resources