Render Picker Items from React-Native picker to populate drop-menu selections - reactjs

I am successfully using the react-native picker to render a drop-menu in one of my components. Right now, in the picker code I am hardcoding the values used to populate the drop-menu items. What I'd ideally like to do is pass these in as props, and have them dynamically generate as many items as necessary within the picker. I'm not sure how to do that, though. I tried using a for-loop but I can't run that kind of conditional logic within the component code itself. This is what it looks like with hard coded values.
export const DropDownMenu = (props) => {
const [selectedValue, setSelectedValue] = useState(null);
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{ height: 50, width: 150 }}
onValueChange={(itemValue, itemIndex) => {
props.onSelectMenuValue(itemValue),
setSelectedValue(itemValue)
}}
>
<Picker.Item label="A" value="a" />
<Picker.Item label="B" value="b" />
<Picker.Item label="C" value="c" />
</Picker>
</View>
);
}
How could I render Picker.Item values based on the props passed in?

You can pass an options prop as an array of objects with a label and a value property and use map like this:
<Picker /*whatever you need for picker here*/>
{props.options.map(option => <Picker.Item label={option.label} value={option.value}/>)}
</Picker>

Related

What does this notation mean in React-Native?

I took over an unfinished react-native project and this notation confused me. An anonymous function is written in curly brackets and given various arguments ( handleChange, handleBlur, handleSubmit, values). Where do we define those arguments ? Where can an anonymous function get the data for these arguments? What exactly is the function of these arguments?
This codeblock was inside the JSX template between various react-native elements.
Entire project was built along TypeScript.
return (
<ScrollView
contentContainerStyle={{
flex: 1,
flexGrow: 1,
padding: spacing.xl,
}}
>
<KeyboardAvoidingView style={{ flex: 1 }}>
<TouchableWithoutFeedback
onPress={Keyboard.dismiss}
style={{ flex: 1 }}
>
<ImageBackground
source={require('#/assets/images/text-bg.png')}
resizeMode="cover"
minHeight="100%"
flex={1}
>
<Box flex={1}>
<Text color="loginHeader" fontSize={36} marginBottom="xl">
Login
</Text>
<Formik
initialValues={{ phoneNumber: '' }}
onSubmit={async values => await submitLogin(values)}
>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<>
<Box width="100%" marginBottom="xl">
<TextInput
keyboardType="phone-pad"
placeholder="Phone Number"
placeholderTextColor={colors.neutral500}
value={values.phoneNumber}
onChangeText={handleChange('phoneNumber')}
onBlur={handleBlur('phoneNumber')}
/>
</Box>
<Button
label="Login"
onPress={handleSubmit}
backgroundColor="buttonBackground"
padding="md"
borderRadius="sm"
shadowColor="black"
shadowOpacity={0.4}
shadowRadius={8.3}
elevation={20}
shadowOffset={{ width: 0, height: 6 }}
/>
</>
)}
{/* <Box width="100%" marginBottom="xl">
<TextInput
secureTextEntry={true}
placeholder="Password"
placeholderTextColor={colors.neutral500}
/>
</Box> */}
</Formik>
</Box>
</ImageBackground>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
</ScrollView>
)
This technique is known as render props. Instead of passing JSX to an element as it's children, you pass a function which takes data from the parent as its arguments. In your example, Formik calls its children prop with those arguments - they are controlled and defined by the Formik component.
This anonymous function is getting passed to another component as children prop, which is a variant of render prop pattern, a pattern used to share functionality between components.
Like the piece of code you posted, a component like Formik manages some functionality for an input, all of which are encapsulated within the Formik component. to share them, it asks you for a prop that should be function, and can be named anything, but mostly render or children.
Inside itself, the Formik component is structured like this:
const handleChange ... // some logic about input being changed
later in it its return it calls that function props you passed like this:
return <>{props.render({handleChange})</>
In your case, Formik is using the children prop, like this:
return <>{props.children({handleChange})</>

Date component passing the value to parent component react typescript

I want to create a date component on a child component and show on the screen (on the parent component) the selected date. Moreover I have two problems:
I am not managing to make the components talk with each other. I can print on the console the answer from the child component but I cannot pass to the parent component
When I select date "custom" on my dropdown I want to select a start and end date but I can't do it, when I select one of the dates (start or the end) the other updates itself automatically.
Any help is welcome :)
Here is my codesandbox: https://codesandbox.io/s/date-picker-forked-h1u9s?file=/src/App.tsx
This is how I call the date component
export default function App() {
return (
<div>
<DateComponent />
<div>Your chosen date was: </div>
</div>
);
}
This is the date component:
return (
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<div className="App">
<div>
<ReactSelect
value={selectedOption as ValueType<OptionType>}
onChange={(option) => handleChange(option)}
isMulti={false}
options={options}
/>
{selectedOption === DateValueEnum.Custom ? (
<div style={{ display: "flex" }}>
<div style={{ width: "50%", float: "left", paddingRight: "5px" }}>
<DatePicker
fullWidth
margin="normal"
required={true}
error={false}
invalidLabel={"Several values..."}
value={selectedDate}
onChange={(newDate) => setSelectedDate(newDate)}
format="MM/dd/yyyy"
/>
</div>
<DatePicker
fullWidth
margin="normal"
required={true}
error={false}
invalidLabel={"Several values..."}
value={selectedDate}
onChange={(newDate) => setSelectedDate(newDate)}
format="MM/dd/yyyy"
/>
</div>
) : null}
</div>
</div>
</MuiPickersUtilsProvider>
);
};
You can share state or function between components.
There is many ways to do it
You can share state directly from Parent component to children, before sharing state you need to create one with React useState hook
like so:
const [date, setDate] = useState("")
<DateComponent setDate={setDate} />
Here we passed the setDate function to the children component.
When we will call that function in DateComponent we will change the Parrent component's state
props.setDate("date")
read about state and life cycle here, it will help you a lot.
Also read about hooks, which were used previosly.
Another way to pass data between components is useContext it's more complicated way, or you can use library like Redux, Mobx.
About "custom" selected option, you created only one state for two options, so when you pick the date, both are changed. To fix it you nedd to add second state.
here the fixed version: sandbox

React-select options shows default blue background

I have created a select component using react-select. I need to append a badge to the option who's available value is false. I have achieved it using -
const formatOptionLabel = ({ code, name, available }) => (
<div className="ds-select-distibutor-step-container format-option" style={{ display: "flex", cursor:"none" }}>
<div>{name}</div>
{!available ?
<div className="format-label" style={{ borderRadius: "12px" ,marginLeft: "auto", color: "#fff" , backgroundColor: "#aaa", padding: "2px 10px", fontSize:"12px"}}>
<span>Coming Soon</span>
</div>
: ''}
</div>
);
Following json comes at run time, which I map and use as options-
const options= [
{code:"abc",name:"abc",rank:0,available:true},
{code:"xyz",name:"xyz",rank:0,available:false},
{code:"pqr",name:"pqr",rank:0,available:true}]
And following is my custom select component
<Field
id="selectedDistributor"
name="selectedDistributor"
component={customSelect}
options={sortBy(options, 'name').map(({ code, name, available}) => ({ code, name, available }))}
formatOptionLabel={formatOptionLabel}
className="select-step"
placeholder="abc"
touched={false}
defaultValue="abc"
requiredField
>
</Field>
<FieldWrapper
label={label}
requiredField={requiredField}
touched={touched}
forId={inputId}
>
<Select
{...input}
id={inputId}
disabled={isEditMode || disabled}
onChange={value => input.onChange(value.code)}
formatOptionLabel={formatOptionLabel}
options={options}
placeholder={placeholder}
value={input.code}
className={`input-components-select ${className}`}
>
</Select>
</FieldWrapper>
enter code here
Everything is almost working but when I select the option, the background of all the options turns into blue, I tried making changes and found that if I pass the 'value' as a key of JSON instead of 'name' in the option this error goes away. But I can't use it since the json is coming from the backend and its dynamic.
Could anybody please suggest what should be done so that the background doesn't change? I have attached images for reference
first time
after selecting
I faced the same problem and I found that you need to declare a function getOptionValue as the Select's props. In your example, it should be like this
<Select
{...input}
id={inputId}
disabled={isEditMode || disabled}
onChange={value => input.onChange(value.code)}
formatOptionLabel={formatOptionLabel}
options={options}
placeholder={placeholder}
value={input.code}
getOptionValue={(option) => option.code} // changes here!!!
className={`input-components-select ${className}`}
>
</Select>
This function is used for comparison to highlight the currently selected option in the list. You can check more details in the discussion here
https://github.com/JedWatson/react-select/issues/3388

How to prevent re-render while keeping state when using Material UI Autocomplete with multiple options?

I am using Material UI's Autocomplete component with multiple options enabled. I need to be able to select multiple options and update the state every time the value changes. For this, I am using the onChange prop. This works fine when using only one option. But with multiple options enabled, every time I select an option, the component re-renders and I am unable to select a second option.
This is the onChange handler:
const handleUserAssignEvent = (event, value) => {
setSelectedUsers(value)
}
This is the Autocomplete component:
const renderUserAssignmentForm = () => {
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />
const checkedIcon = <CheckBoxIcon fontSize="small" />
return (
<Autocomplete
multiple
id="usersList"
options={filteredUsers ?? users ?? []}
noOptionsText="No users found..."
disableCloseOnSelect
onChange={(event, value) => handleUserAssignEvent(event, value)}
getOptionLabel={(option) => option.fullname}
renderOption={(option, { selected }) => (
<>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.fullname}
</>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Users"
placeholder="User lookup..."
/>
)}
/>
)}
What can I do to be able to update the state without having the Autocomplete component re-render on every change and be able to select multiple options?
I've replicated your example here and there was no problem with selecting multiple options.
Your problem might be that the options prop filteredUsers and users are related/change with selectedUsers.
Or the whole component which contains renderUserAssignmentForm being unmounted and mounted again.

How can I force sorting arrows to always display in a react-admin Datagrid?

It doesn't feel like best practice, but my users want to always see the arrows next to every column that's sortable in a list view. I'm building a web app in react, using the react-admin Datagrid object:
export const PermitList: FunctionComponent<FullProps> = (props) => {
const { hasShow, ...rest } = props;
return (
<Datagrid {...rest}>
<TextInput source="permitID" />
<TextInput source="dept" />
<TextInput source="workOrder" />
<TextInput source="status" getColor={getColorFromStatus} />
<ListButtons hasShow={hasShow!} label="Edit Permit" canDelete={false} />
</Datagrid>
);
};
Fields permitID, dept, workOrder, and status are all sortable. The sort arrow appears by default when the grid header is clicked and the field sorts, but my users want to see all sort arrows all the time. I haven't been able to find any documentation on this. Is there a way to display it using Datagrid, or do I need to use a different grid object?
You can manually add your own arrow elements using headerClassName for each sortable field:
const useListStyles = makeStyles(theme => ({
myHeader: {
"&&:before": {
content: '" ⇅ "',
color: 'red',
},
},
}))
const CardList = (props) => {
const classes = useListStyles()
return (
<List {...props} >
<Datagrid>
<TextField source="id" />
<TextField source="name" headerClassName={classes.myHeader} />
</Datagrid>
</List>
)
}

Resources