How can I use react-native-dropdown-picker(DropDownPicker) without open, setOpen properties? - react-native

const [openDropdown, setOpenDropdown] = useState(false);
<Controller
control={control}
name={EPaymentFormField.PROMOTION_COUPON_ID}
render={({ field: { value, onChange } }) => {
const handleChange = (callback: any) => {
onChange(callback());
};
return (
<DropdownPicker
placeholder={t('paymentDetail_coupon_label', {
numOfAvailableCoupons: numOfAvailableCoupons,
numOfTotalCoupons: numOfTotalCoupons,
})}
open={openDropdown}
setOpen={setOpenDropdown}
value={value}
setValue={handleChange}
items={dropdownData}
multiple={false}
/>
);
}}
/>
How can I use without open, setOpen properties on DropDownPicker?
these are essential properties in this library.
But I don't want to use useState.
So, If it can, I want to use it without open, setOpen properties or useState.
Is anyone knows solution?

I don't think the lib forces you to use useState
You should be able to pass a boolean is and a random function to like so :
<DropdownPicker
placeholder={t('paymentDetail_coupon_label', {
numOfAvailableCoupons: numOfAvailableCoupons,
numOfTotalCoupons: numOfTotalCoupons,
})}
open={true} // if you want to keep it open
setOpen={() => return;}
value={value}
setValue={handleChange}
items={dropdownData}
multiple={false}
/>

Related

Implementing react-hook-forms and react-select with radio buttons and multi inputs combo

Ready for a complicated one?
Using react-hook-forms and react-select using creatable (user creates multiple inputs on the fly)
I'm trying to implement a form that uses an option on 4 radio buttons, 2 of which reveal multi inputs (inputs that use react-select where the user can create multiple entries, not a dropdown) and trying to keep track of both the radio inputs and the multi inputs in the final useForm() object. I also need to be able to remove them if the user changes their mind or resets the form in total. Right now, the key values of registrationTypes changes when I change radioTwo and enter inputs. I also don't know how to remove user inputs. I'm using Controller to read the entries (although I've heard if you're using native HTML checkbox input, you have to use Register?) . Here's the code:
import styled from 'styled-components'
import Creatable from 'react-select/creatable'
import { Controller } from 'react-hook-form'
import { ErrorRow } from '../util/FormStyles'
import { FormRulesProps } from '../util/formRuleTypes'
import FormError from './FormError'
const registrationTypes = [
{
label: 'Allow anyone with the link to register',
value: 'radioOne',
},
{
label: 'Allow anyone with this email domain to register:',
value: 'radioTwo',
},
{ label: 'Allow anyone with this code to register:', value: 'radioThree' },
{
label: 'Define eligible users manually through eligibilty file.',
value: 'radioFour',
},
]
const RegistrationEligibilty = () => {
const {
control,
reset,
handleSubmit,
formState: { errors },
} = useForm<any>()
const [selectedRadio, setSelectedRadio] = useState({ regType: '' })
const onSubmit = (data: any) => {
console.log(data)
}
const handleSelected = (value: string) => {
setSelectedRadio({ ...selectedRadio, regType: value })
}
return (
<RegistrationEligibiltyContainer>
<form onSubmit={handleSubmit(onSubmit)}>
<FormRow>
<RadioWrapper>
{registrationTypes.map((rt) => (
<Controller
key={rt.value}
control={control}
name="radio"
render={({ field: { onChange, value } }: any) => (
<RadioGroup
// #ts-ignore
value={value}
onChange={(e: any) =>
onChange(e.target.value, handleSelected(rt.value))
}
>
<Radio
name={rt.value}
type="radio"
value={rt.value}
checked={selectedRadio.regType === rt.value}
/>
<TextRow
text={rt.label}
style={{ paddingLeft: '10px' }}
/>
{selectedRadio.regType === 'radioTwo' &&
rt.value === 'radioTwo' && (
<FormRow>
<div style={{ width: '90%' }}>
<Controller
name={rt.value}
control={control}
render={({ field }) => (
<Creatable
{...field}
isMulti
options={field.value}
value={field.value}
placeholder="Select domains"
/>
)}
/>
</div>
</FormRow>
)}
{selectedRadio.regType === 'radioThree' &&
rt.value === 'radioThree' && (
<FormRow>
<div style={{ width: '90%' }}>
<Controller
name={rt.value}
control={control}
render={({ field }) => (
<Creatable
{...field}
isMulti
options={field.value}
value={field.value}
placeholder="Select codes"
/>
)}
/>
</div>
</FormRow>
)}
</RadioGroup>
)}
/>
))}
</RadioWrapper>
</FormRow>
</FormContent>
</form>
</RegistrationEligibiltyContainer>
)
}
export default RegistrationEligibilty
The result looks like this:
https://imgur.com/a/6oGhqRb
I also need to be able to remove them if the user changes their mind or resets the form in total
If the component is unmounted, the value of the component is not included in the result. Make sure your Creatable is unmounted when the relevant radio is not selected

How to create a custom record action button inside a List component with React-Admin?

I'm a totally newbie with React and React-Admin. IMHO, I'm trying to achieve something simple that many people must have already done but I cannot find any kind of tutorial anywhere.
I'd like to add another button to the list of action buttons (show/edit) within each row in a <List> component. This button would archive the record.
My last try looks like the code below.
import React from 'react';
import {
Datagrid,
EmailField,
List,
TextField,
ShowButton,
EditButton,
DeleteButton,
CloneButton,
} from 'react-admin';
import { makeStyles } from '#material-ui/core/styles';
import ArchiveIcon from '#material-ui/icons/Archive';
const useRowActionToolbarStyles = makeStyles({
toolbar: {
alignItems: 'center',
float: 'right',
width: '160px',
marginTop: -1,
marginBottom: -1,
},
icon_action_button: {
minWidth: '40px;'
},
});
const ArchiveButton = props => {
const transform = data => ({
...data,
archived: true
});
return <CloneButton {...props} transform={transform} />;
}
const RowActionToolbar = (props) => {
const classes = useRowActionToolbarStyles();
return (
<div className={classes.toolbar}>
<ShowButton label="" basePath={props.basePath} record={props.record} className={classes.icon_action_button}/>
<EditButton label="" basePath={props.basePath} record={props.record} className={classes.icon_action_button}/>
<ArchiveButton {...props} basePath={props.basePath} label="" icon={<ArchiveIcon/>} record={props.record} className={classes.icon_action_button} />
<DeleteButton basePath={props.basePath} label="" record={props.record} className={classes.icon_action_button}/>
</div>
);
};
export const UserList = props => {
return (
<List
{...props}
sort={{ field: 'first_name', order: 'ASC' }}
>
<Datagrid>
<TextField source="first_name"/>
<TextField source="last_name"/>
<EmailField source="email"/>
<RowActionToolbar/>
</Datagrid>
</List>
)
};
Obviously, this code does not work because the <CloneButton> component get rid of the id the record. Moreover, except if I did something wrong - which is totally possible -, it makes a GET request to a create endpoint.
I'm using different routes in my dataProvider (The back end is using Django and Django rest framework). I want to send a PATCH to the detail endpoint, like the <Edit> component does.
I also tried with a <SaveButton>, but it fails too.
Uncaught TypeError: Cannot read property 'save' of undefined
at useSaveContext (SaveContext.js:23)
I guess the <SaveButton> must be within a <SimpleForm>?
I'd like the save behaviour of the <DeleteButton>, i.e. update the record from the list, display the notification that the record has been archived (with the Undo link), send the request to the back end, refresh the list.
Any guidance, directions would be very appreciated.
I don't know that this is a full answer, but felt like more than a comment...
You are trying to archive the existing record, not create a whole new record, right? CloneButton is supposed to be used to create a new record with a new ID (which is why your ID is going away), so you don't want to us it here. note that I've never used CloneButton. it is not fully documented so I could be wrong about its use.
I am thinking that you should use the useRecordContext hook within your Archive button to pull in all of the record's data, including the id; read this little section: https://marmelab.com/react-admin/Architecture.html#context-pull-dont-push
And I don't think transform is what you're looking for here. You will need to use one of the dataProvider hooks, i'm assuming useUpdate: https://marmelab.com/react-admin/Actions.html#useupdate
//first create component
const MyButton = (props: any) => {
const [sendEmailLoading, setSendEmailLoading] =
React.useState<boolean>(false);
const record = useRecordContext(props);
const sendEmail = (id: Identifier) => {
setSendEmailLoading(true)
dataProvider.sendEmail(
"notifications", { id: id })
.then(({ data }: any) => {
if (data && data.status == "success")
notify('Email send success', { type: 'success' });
setSendEmailLoading(false);
refresh();
});
};
return (
<ButtonMUI color='primary' size="small" onClick={() => {
sendEmail(record.id) }}>
{
!record.publish &&(
!sendEmailLoading ? (
translate('resources.notifications.buttons.send')
) : (
<CircularProgress size={25} thickness={2} />
)
)
}
</ButtonMUI>
)
}
//and second add to datagrid list
<Datagrid>
<NumberField source="id" />
<TextFieldRA source="subject" />
<DateField source="date" />
<BooleanField source="publish" />
{/* <EditButton /> */}
<ShowButton />
<MyButton />
</Datagrid>

React-native Formik setFieldValue

Here is a simplified version of my code.
Notice the setFieldValue_ and this.setFieldValue_ = setFieldValue;
This code works fine, I'm able to get the output when submit button is clicked.
I'm actually wondering if this is the right way to do it? If not, can you point me to the right direction? Also what is this method called? (assigning class variable to some function and use it within another function)
class MyComponent extends React.Component {
setFieldValue_;
someFunction() {
this.setFieldValue_("name", value);
}
render() {
return (
<Formik
initialValues={{
something: ""
}}
onSubmit={values => console.log(values)}
>
{({
setFieldValue,
}) => {
this.setFieldValue_ = setFieldValue;
<ThirdPartyCustomComponent onChange={this.someFunction} />
}}
</Formik>
}
}
I would personally have the onChange simply call formik set field value there and then rather than using different functions. Strictly speaking you don't want to set the value like that because every re-render is setting the value again.
I would also recommend looking at custom formik inputs using the useField hook - https://jaredpalmer.com/formik/docs/api/useField. This will allow you to write a small wrapper around your third party component and formik. Noticing you have used a class based component you may want to do some short reading into react hooks before throwing yourself into using useField.
Docs example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className='error'>{meta.error}</div>
) : null}
</>
);
};

How to check checkbox when there is an array in React Native?

I want to get checked friends using checkbox. But I not quite sure how i will achieve it, hope someone can help me.
This is my state:
state = {checked: false}
This is where I want to map array
{this.props.navigation.getParam('friends').map((name, key) => (
<View>
<Text>{name}</Text>
<CheckBox
checked={this.state.checked}
onPress={(val)=>{}}
/>
</View>))}
Note: Or Could someone write me an app/code snippet in snack.expo.io how to get only checked checkbox value
You can write a custom checkbox component
export default class CustomCheckbox extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
};
}
toggleChange(){
this.setState({checked: !this.state.checked});
}
render() {
return (
<View>
<Text>{this.props.name}</Text>
<CheckBox
checked={this.state.checked}
onPress={() => this.bind.toggleChange(this)}
/>
</View>
);
}
}
and import your CustomCheckbox component
import CustomCheckbox from "your CustomCheckbox.js path"
{this.props.navigation.getParam('friends').map((name, key) => (
<View>
<CustomCheckbox name={name} />
</View>
))}
Your code is pretty good to go, you just need to update a bit. You have following two options:
Your friend's array should have checked key within each containing object, then you can simply do something like this.
{
this.props.navigation.getParam('friends').map((item, key) => (
let {name, checked} = item // item is an object from friends array,the and it have name, checked and other keys
<View>
<Text>{name}</Text>
<CheckBox
checked={checked}
onPress={(val)=>{}}
/>
</View>))
}
Other is you to save the name of the person as key and true/false as the checked state, eg :
toggleCurrentFirendState = (item)=>{
this.setState((prevState)=>{
let {name} = item //get name from clicked friend from the list
return {
...prevState, //used spread operator, so that other states doesn't get mutat.
[name]:!prevState[name] //toogle state of clicked item
}
})
}
//within your render
{
this.props.navigation.getParam('friends').map((item, key) => (
let {name} = item // item is an object from friends array,the and it have name, checked and other keys
<View>
<Text>{name}</Text>
<CheckBox
checked={name ===this.state[name]} //see change
onPress={(val)=>{this.toggleCurrentFirendState(item)}}
/>
</View>))
}

Update Input Value while mapping React Native

I am creating react-native mobile app. I have an array with some values. I want to set array's value into input field. I have added value in the fields but i can't able to update these values. I have set my values in a qty variable like this:
constructor(props) {
super(props);
this.state = {
qty:[],
}
}
componentWillMount() {
var ids = [];
this.props.data.map((dataImage,Index) => {
dataImage['pro-name'] != undefined && (
ids.push({'qty':dataImage['pro-qty']})
)
})
this.setState({qty:ids})
}
render() {
return {
this.props.data.map((dataImage,Index)=>(
<View key={Index} style={productStyle.cartview}>
{dataImage['pro-name'] && (
<View style={productStyle.cartmain}>
<Input value={this.state.qty[Index]['qty']} onChange={this.handleChangeInput.bind(this)} style={{width:40,height:40,textAlign:'left'}} />
</View>
)}
</View>
))
}
}
Its showing values properly into the input field but i can't able to type anything into the field to update the values. what can i do for this
I will suggest you to move your input container into separate class, its better approach and each component will handle its own state. Its easy to handle and will result better in performance too.
components = []
render() {
return this.props.data.map((item, index) => (
<CustomComponent data={item} index={index} ref={(ref) => this.components[index] = ref} />
))
}
You can then get child (CustomComponent) value from its ref.
this.components[index].getValue()
this.components[index].setValue('Value');
You will need to create these functions (getValue & setValue) in CustomComponent class.
solution
Here is solution to your query. You need to install lodash or find other solution to make a new copy qty.
<Input onChangeText={(text) => this.handleChangeText(text, index)} />
handleChangeText = (text, index) => {
const qty = _.cloneDeep(this.state.qty);
qty[index] = {
qty: text
}
this.setState({qty})
}
Your Input's value is set to this.state.qty[Index]['qty']. And to change it on text edit, you can do it like this. You do not need to bind the function, instead use an arrow function like this.
onChangeText={ (newValue) => {
this.setState({ <your-input-value-state>:newValue })
}}
You have to update the value of each Input individually on onChange event.
Replace your with Input with this
<Input value={this.state.qty[Index]['qty']}
onChange={this.handleChangeInput.bind(this, Index)}
style={{width:40,height:40,textAlign:'left'}}
/>
and update the state accordingly with the Index when the event is called
handleChangeInput(index, value){
let {qty} = this.state;
let qty_update = qty.slice();
qty_update[index]['qty'] = value;
this.setState({qty: qty_update});
}