React Native: Radio Button group with optional text input - reactjs

I need to build component from the picture. Basically it's regular radio button component, but when the user selects button named 'Other' text input should appear where user can enter custom value.
I already have built the group, I'm just not sure how handle the TextInput part.
What is the best way to structure the component like this?

import React from "react";
export const Radio = () => {
const [isOtherSelected, setOtherIsSelected] = React.useState(0);
const [value, setValue] = React.useState("0");
return (
<div>
<input
type="radio"
value="Other"
checked={isOtherSelected}
onChange={setOtherIsSelected(!isOtherSelected)}
/>
{isOtherSelected ? (
<input value={value} onChange={(e) => setValue(e.target.value)}></input>
) : (
""
)}
</div>
);
};
Just render text input if "other option" is checked. Control value of this input in state.

Simple minified example
import * as React from 'react';
import { View } from 'react-native';
import { RadioButton, Text ,TextInput} from 'react-native-paper';
const MyComponent = () => {
const [value, setValue] = React.useState('rent');
const [otherPayment,setOtherPayment] = React.useState('');
return (
<View style={{padding:15}}>
<RadioButton.Group onValueChange={newValue => setValue(newValue)} value={value}>
<View style={{flexDirection:'row',alignItems:"center"}}>
<Text>Rent</Text>
<RadioButton value="rent" />
</View>
<View style={{flexDirection:'row',alignItems:"center"}}>
<Text>Other</Text>
<RadioButton value="other" />
</View>
</RadioButton.Group>
{value === 'other' && <TextInput placeholder="Other Payment method" onChangeText={text=> setOtherPayment(text)}/>}
</View>
);
};
export default MyComponent;
Expo snack - https://snack.expo.dev/#emmbyiringiro/77f4e2

Related

Accessing value of child component within Formik form

I have a form that uses Formik and contains two fields - email and siteID
The email field uses a TextInput whereas siteID comes from my own component called DropDown that uses react-native-dropdown-picker
When my form is submitted I can see the value of email within handleSubmit() but siteID remains as 0 and not the selected value.
How can I access the selected value from my DropDown child component when submitting the parent form?
My form
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.loginContainer}>
<Formik
validationSchema={loginValidationSchema}
initialValues={{
siteID: 0,
email: "",
}}
onSubmit={(values) => handleSubmit(values)}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
errors,
isValid,
}) => (
<>
<Text style={styles.label}>Email</Text>
<TextInput
name="email"
style={styles.textInput}
onChangeText={handleChange("email")}
onBlur={handleBlur("email")}
value={values.email}
keyboardType="email-address"
/>
<DropDown name="siteID" />
</>
)}
</Formik>
</View>
</SafeAreaView>
);
DropDown component
import * as React from 'react';
import {StyleSheet} from 'react-native';
import DropDownPicker from 'react-native-dropdown-picker';
import {usePostRequest} from '../../client';
import {useState, useEffect} from 'react';
const DropDown = () => {
const [pickeropen, pickersetOpen] = useState(false);
const [pickervalue, pickersetValue] = useState(null);
const [pickeritems, pickersetItems] = useState([]);
const {status: siteStatus, data: siteData} = usePostRequest('/api/sites', {});
useEffect(() => {
console.log('siteData', siteData);
if (siteData.count) {
const sitesList = [];
siteData.results.map((item, key) =>
sitesList.push({label: item.siteName, value: item.siteID})
);
pickersetItems(sitesList);
}
}, [siteData]);
return (
<DropDownPicker
style={styles.textInput}
placeholder="Please chose a site"
open={pickeropen}
value={pickervalue}
items={pickeritems}
setOpen={pickersetOpen}
setValue={pickersetValue}
setItems={pickersetItems}
/>
);
};
Based on the docs, to use your own component, you should wrap your component in a Field as follows:
const DropDownField = <Field name="siteId" component={DropDown} />
When using a Field formik will inject props (name, value, onChange, onBlur) into your component allowing you to control the form state.
So you can then redeclare your component with those props as follows:
import * as React from 'react';
import {StyleSheet} from 'react-native';
import DropDownPicker from 'react-native-dropdown-picker';
import {usePostRequest} from '../../client';
import {useState, useEffect} from 'react';
const DropDown = ({ field: { name, value, onChange }) => {
const [pickeropen, pickersetOpen] = useState(false);
const [pickeritems, pickersetItems] = useState([]);
const {status: siteStatus, data: siteData} = usePostRequest('/api/sites', {});
useEffect(() => {
console.log('siteData', siteData);
if (siteData.count) {
const sitesList = [];
siteData.results.map((item, key) =>
sitesList.push({label: item.siteName, value: item.siteID})
);
pickersetItems(sitesList);
}
}, [siteData]);
return (
<DropDownPicker
style={styles.textInput}
placeholder="Please chose a site"
open={pickeropen}
value={value}
items={pickeritems}
setOpen={pickersetOpen}
setValue={onChange}
setItems={pickersetItems}
name={name}
/>
);
};
Essentially you wire your component up to the onChange and value props. Since you assigned a name of siteId to the DropDownField component (which you should now use in your form), any time you call onChange site Id will update.
Link to docs
You can use setFieldValue provided by Formik to set a field manually.
A good example of usage is provided in this example
You can pass setFieldValue to your DropDown Component:
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.loginContainer}>
<Formik
validationSchema={loginValidationSchema}
initialValues={{
siteID: 0,
email: "",
}}
onSubmit={(values) => handleSubmit(values)}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
errors,
isValid,
setFieldValue,
}) => (
<>
<Text style={styles.label}>Email</Text>
<TextInput
name="email"
style={styles.textInput}
onChangeText={handleChange("email")}
onBlur={handleBlur("email")}
value={values.email}
keyboardType="email-address"
/>
<DropDown name="siteID" setSiteID={setFieldValue} />
</>
)}
</Formik>
</View>
</SafeAreaView>
);
And trigger it from inside of the DropDown Component:
const DropDown = ({ setSiteID }) => {
const [pickeropen, pickersetOpen] = useState(false);
const [pickervalue, pickersetValue] = useState(null);
const [pickeritems, pickersetItems] = useState([]);
const {status: siteStatus, data: siteData} = usePostRequest('/api/sites', {});
useEffect(() => {
console.log('siteData', siteData);
if (siteData.count) {
const sitesList = [];
siteData.results.map((item, key) =>
sitesList.push({label: item.siteName, value: item.siteID})
);
pickersetItems(sitesList);
}
}, [siteData]);
// You can use useEffect or create a custom handleValueChange
// and pass it to DropDownPicker
useEffect(() => {
setSiteID('siteID', pickervalue);
}, [pickersetValue])
return (
<DropDownPicker
style={styles.textInput}
placeholder="Please chose a site"
open={pickeropen}
value={pickervalue}
items={pickeritems}
setOpen={pickersetOpen}
setValue={pickersetValue}
setItems={pickersetItems}
/>
);
};
try this,
get setFieldValue from formik props.
add onChangeDropdown to <Dropdown /> like,
<DropDown name="siteID" onChangeDropdown={(value)=>{
setFieldValue("siteID",value)
}}/>
get the props inside Dropdown component,
const DropDown = (props) => {
add onChangeValue prop to your DropDownPicker
onChangeValue={props.onChangeDropdown}

How can I use ref to get the input value of TextInput in ReactNative?

I want to console log the text in the TextInput once the Button is pressed.
I want to use useRef() to reference the TextInput.I tried this in react and works fine.But it is not working in react native
import { useRef } from "react";
import { Button, TextInput, View } from "react-native";
export default function App() {
const inputRef=useRef();
const send=()=>{
console.log(inputRef.current.value);
}
return(
<View>
<TextInput ref={inputRef}/>
<Button
title="send"
onPress={send}
></Button>
</View>
)
}
You can use a state.
const [value, setValue] = useState("");
function send(){
console.log(value);
}
return (
<TextInput value={value} onChange={setValue}/>
);

Radio button value not resetting

I have a list of questions. And on click of next or previous I'm changing the question. I have 4 or 5 answers as radio buttons. I' using this package Radio button npm. And now I'm trying to change the question using states. I need the radio button unselected on changing of question. All content changing easily expect radio button resetting. Here is my code:-
<ScrollView>
<RenderHtml
contentWidth={width}
source={{html:question}}
/>
<OptionsView options={options} selectedBtn={(e) =>updateAnswer(e.option)}/>
</ScrollView>
Functional component
const OptionsView=(props)=>{
return(
<RadioButtonRN
style={{marginHorizontal:5,marginTop:1,margin:5}}
deactiveColor={'gray'}
activeColor={'green'}
initial={0}
boxStyle={{height:50}}
data={props.options}
icon={
<Icon
name="check-circle"
size={25}
color="green"
/>
}/>
)}
I solved it using below code:-
import React, { useState } from "react";
import { View, StyleSheet, Button, Alert } from "react-native";
import RadioButtonRN from 'radio-buttons-react-native';
const App = () => {
const [show,setShow] = React.useState(true);
const data = [
{
label: 'data 1'
},
{
label: 'data 2'
}
];
React.useEffect(()=>{
if(!show) setShow(true)
},[show])
const resetHandler = () =>{
setShow(false)
}
return (
<View style={styles.container}>
{show &&
<RadioButtonRN
data={data}
selectedBtn={(e) => console.log(e)}
/>
}
<Button title='reset' onPress={resetHandler} />
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingTop:100,
}
});
export default App;
Reference:- Radio Reset Button
If you are rendering your options base on the props that has been passed from parent component , U need to also include this hook in order to update your child component:
const OptionsView = (props.options) => {
const [options, setOptions] = useState(props.options);
useEffect(() => {
setOptions(props.options)
}, [props.options]
return (
<RadioButtonRN/>
)
}

React Make re-usable input field for URL to replace non http to http

I am trying to write a reusable component for the input field for URL
import React from 'react';
const InputURL = ({ nam, ...rest}) => {
return (
<>
<input
name={name}
{...rest}
/>
</>
);
}
export default InputURL;
I want when user input facebook.com then in onChange it should return with http like it will be https//facebook.com
I will use only this <InputURL onChange={(e) => console.log(e)} />
Can anyone please tell me how can i override onChange to replace non http to http?
Note: I want to write the condition once in the InputURL component only, dont want to write everywhere where i will use it
onChange event may trigger many time so it would be better if you make your logic to add "http" on blur event like this.
import React from 'react';
const InputURL = ({ name, callback, ...rest }) => {
const [value, setValue] = React.useState('');
return (
<>
<input
onBlur={() => callback(`http://${value}`)}
name={name}
onChange={(e) => setValue(e.target.value)}
{...rest}
/>
</>
);
};
export default InputURL;
and in the component where you are using this component find parameter coming in callback.
for example I am logging here.
import React from 'react';
import './style.css';
import InputURL from './InputURL';
export default function App() {
const callback = (val) => {
console.log('>>>>>', val);
};
return (
<div>
<InputURL callback={callback} name="My Input" />
</div>
);
}
See SS hope you are looking the same.
import React from 'react';
const InputURL = ({ nam, prefix, ...rest}) => {
return (
<>
<input
name={nam}
id={"input-"+nam}
onChange={(e) => {
const value = e.target.value;
const input = document.querySelector('#input-'+nam);
if(value.substring(0,prefix.length) != prefix)
{
input.value = prefix;
}
}}
{...rest}
/>
</>
);
}
export default InputURL;
This should work, check it out the reference of Substring here.

Focusing the next input after reaching the max length React Native

I want to be able to move the focus from one input onto the next after the user has entered 2 numbers in the input box. Instead of the user manually clicking the next field I'd like the focus to switch there automatically.
Below is how I've currently implemented my two input fields.
I've tried the things mentioned here using refs but doesn't seem to work at all https://www.codegrepper.com/code-examples/csharp/react+native+textinput+focus
<Controller
control={form.control}
defaultValue={initialMonthValue}
name={monthName}
render={({ onChange, onBlur, value }) =>
<Input
containerStyle={{ width:80 }}
editable={!isSubmitting}
autoCapitalize="none"
numberOfLines={1}
multiline={false}
keyboardType='number-pad'
placeholder="MM"
maxLength={2}
placeholderTextColor={Colors.Grey}
onChangeText={onChange}
onBlur={onBlur}
secureTextEntry={false}
invalid={!!monthError}
value={value} />}
/>
<Slash text="/" />
<Controller
control={form.control}
defaultValue={initialYearValue}
name={yearName}
render={({ onChange, onBlur, value }) =>
<Input
containerStyle={{ width:80 }}
editable={!isSubmitting}
autoCapitalize="none"
numberOfLines={1}
multiline={false}
keyboardType='number-pad'
placeholder="YY"
placeholderTextColor={Colors.Grey}
onChangeText={onChange}
onBlur={onBlur}
secureTextEntry={false}
invalid={!!yearError}
value={value} />}
/>
If I understand what are you looking to implement then this should work for you.
import { useRef, useEffect, useState } from "react";
export default function FormInputs() {
const inputRef = useRef(null);
const [length, setLength] = useState(0);
const handleChange = (e) => {
// Handle type checking here
setLength(length + 1);
};
useEffect(() => {
// if the Ref doesn't exist for some reason then break.
if (!inputRef) return;
const input = inputRef.current;
if (length === 2) input.focus();
}, [length]);
return (
<>
<input onChange={handleChange} />
<input ref={inputRef} />
</>
);
}
https://codesandbox.io/s/wonderful-platform-no55n?file=/src/App.js:77-590
if you would love me to help you implement this on your code then don't hesitate to share the components needed for the code to be functional.
You can use TextInput's focus function. Here is a working example:
import React from 'react';
import {SafeAreaView, StyleSheet, TextInput} from 'react-native';
const App = () => {
const [textInputValue1, setTextInputValue1] = React.useState('');
const textInputRef2 = React.useRef<TextInput | null>();
const [textInputValue2, setTextInputValue2] = React.useState('');
return (
<SafeAreaView>
<TextInput
style={styles.textInput}
value={textInputValue1}
onChangeText={(value) => {
if (value.length >= 2) {
textInputRef2.current?.focus(); // Focus on the other TextInput
}
setTextInputValue1(value);
}}
/>
<TextInput
style={styles.textInput}
ref={(ref) => (textInputRef2.current = ref)}
value={textInputValue2}
onChangeText={(value) => setTextInputValue2(value)}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
textInput: {
backgroundColor: 'gray',
margin: 20,
minHeight: 40,
},
});
export default App;
You can focus other Input's ref when you reach maxLength on first input' maxLength prop's value like this:
import React, {useRef} from 'react';
import {View, TextInput} from 'react-native';
const App = () => {
const secondTextInputRef = useRef(null);
const [firstTextInputValue, setFirstTextInputValue] =
React.useState('');
const [secondTextInputValue, setSecondTextInputValue] =
React.useState('');
const maxInputSize = 3;
const App = () => {
return (
<View>
<TextInput
value={firstTextInputValue}
maxLength={maxInputSize}
onChangeText={(value) => {
setFirstTextInputValue(value);
if (value.length >= maxInputSize) {
secondTextInputRef.current?.focus();
}
}}/>
<TextInput
ref={(ref) => (secondTextInputRef.current = ref)}
value={secondTextInputValue}
onChangeText={(value) => setSecondTextInputValue(value)} />
</View>
);
};
export default App;

Resources