Unable to reset all the choices in SelectInput - react-admin

The user needs to select a project in the AutocompleteInput first, doing that will set the filter property on the ReferenceInput which will load the possible values from the server into the SelectInput choices list. The data is fetched from the server however, if the choice that was selected in the SelectInput is no longer in the list, it is not reset. Furthermore the selected value is still in the choices list even though it was not returned from the rest service.
This is the code i wrote that has this issue:
<ReferenceInput label="Project" source="projectId" reference="projecten" filterToQuery={searchText => ({ naam: searchText })}>
<AutocompleteInput optionText="naam" optionValue="id" inputValueMatcher={() => null} />
</ReferenceInput>
<FormDataConsumer>
{({ formData, ...rest }) => {
return <ReferenceInput label="Lot" source="lotId" reference="loten" filter={{ projectId: formData.projectId }} {...rest}>
<SelectInput optionText="lotNummer"/>
</ReferenceInput>
}}
</FormDataConsumer>
How do I reset the SelectInput onChange of the AutocompleteInput and not load the currently selected value?

I know it's a late reply, but it might help someone. I achieved the same effect by using following steps on two SelectInputs inside a ReferenceInput each. I hope it works for your structure as well.
import change from redux-form
import { change } from redux-form
Give your SimpleForm a name
<SimpleForm form="myForm"...
Move your first ReferenceInput inside the FormDataConsumer
<FormDataConsumer>
{({ formData, ...rest }) => {
return (
<div>
<ReferenceInput label="Project" ...
<ReferenceInput label="Lot" ...
</div>
);
}
</FormDataConsumer>
Add an onChange to your first ReferenceInput
<ReferenceInput
label="Project" source="projectId" reference="projecten"
filterToQuery={searchText => ({ naam: searchText })}
onChange={() => {rest.dispatch(change('myForm', 'lotId', ''))}} >
Add a key property to your second ReferenceInput and use your first ReferenceInput's source as it's value
<ReferenceInput label="Lot" source="lotId"
reference="loten" filter={{ projectId: formData.projectId }}
key={formData.projectId} {...rest}>
Finally you'll get
<SimpleForm name="myForm">
<FormDataConsumer>
{({ formData, ...rest }) => {
return (
<div>
<ReferenceInput label="Project" source="projectId"
reference="projecten"
filterToQuery={searchText => ({ naam: searchText })}
onChange={() => {rest.dispatch(change('myForm', 'lotId', ''))}} >
<AutocompleteInput optionText="naam" optionValue="id"
inputValueMatcher={() => null} />
</ReferenceInput>
<ReferenceInput label="Lot" source="lotId" reference="loten"
filter={{ projectId: formData.projectId }}
key={formData.projectId} {...rest} >
<SelectInput optionText="lotNummer" />
</ReferenceInput>
</div>
);
}}
</FormDataConsumer>
</SimpleForm>
I used the steps from this link and added the key property according to another SO question but I'm unable to find it now.

Related

react-hook-form useFieldArray with React-Native

Is it possible to use ReactNative with useFieldArray from react-hook-form? I couldn't find any reference for that.
I'm trying to use it but I can't get the output data from the TextInputs. It looks like the onChange is not working, I tried to use the onChange prop but still couldn't make it.
Example:
const {
control,
formState: {errors},
register,
getValues,
handleSubmit,
} = useForm({
defaultValues: {
playerNameInput: [{playerName: ''}, {playerName: ''}],
},
});
const {fields, append, remove} = useFieldArray({
control,
name: 'playerNameInput',
});
return(
<>
{fields.map((player, index) => (
<View>
<Controller
control={control}
rules={{required: true}}
name="playerNameInput"
render={() => (
<TextInput
style={{width: 100}}
defaultValue={`${player.playerName}`}
{...register(`playerNameInput.${index}.playerName`)}
/>
)}
/>
</View>)}
<Button onPress={handleSubmit(data => console.log(data))}>
Save Changes
</Button>
</>
)

Passing state via route.params with React Navigation returning undefined

I'm attempting to pass a 'passcode' state as params in my React Native app.
I'm doing this by passing params into a 'navigation.navigate' call.
However, every time I navigate to the next screen, it's returning 'undefined' for 'route.params'.
For reference, here is my component I'm passing data FROM:
const SignUpPasscodeScreen = ({ navigation }) => {
const [passcode, setPasscode] = useState(0)
return (
<View>
<View style={styles.mainView}>
<SubLogo />
<Heading title="Set passcode" />
<SubHeading content="You'll need this anytime you need to access your account." />
<Input inputText={ text => setPasscode(text) } inputValue={passcode} />
</View>
<View style={styles.subView}>
<CtaButton text="Continue" onPressFunction={ () => navigation.navigate({ routeName: 'SignUpLegalName', params: { passcode } } ) } />
</View>
</View>
)
}
And here's the component I'm passing data to, and where the error occurs upon navigation:
const SignUpLegalName = ({ route, navigation }) => {
const { passcode } = route.params
return (
<View>
<View style={styles.mainView}>
<SubLogo />
<Heading title="Tell us your name" />
<SubHeading content="This needs to be the same as what's on your passport, or any other form of recognised ID." />
<Input />
<Input />
</View>
<View style={styles.subView}>
<CtaButton text="Continue" onPressFunction={ () => navigation.navigate('SignUpLink')} />
</View>
</View>
)
}
I've tried two forms of passing the props through:
Passing it in as a second argument
Passing it in as a 'params' object as shown above
Both should work according to the documentation - link here
For reference, this is my route structure:
const switchNavigator = createSwitchNavigator({
loginFlow: createStackNavigator({
SignUpPasscode: SignUpPasscodeScreen,
SignUpLegalName: SignUpLegalName,
})
});
The above structure doesn't say to me that it's a nested structure which therefore requires any additional work to pass it through...
Can someone help? It'd be appreciated as it's giving me a headache!
Have a try with below code in the button press event:
<CtaButton
text="Continue"
onPressFunction={() => navigation.navigate('SignUpLegalName',{ passcode })}
/>

How to scope field components based on record properties

In react-admin, how do I access the record properties to show only certain components based on a record property value?
In the following code I want to only show the Bearer Token if the record name is not "All"; if the record name is "All", then only show if the user's role is "admin".
I'm getting "Uncaught TypeError: Cannot read property 'name' of undefined" in the OrgShow component, shown below, which I believe is saying that there is no "props". I've also tried "record" and "name", none of which are available.
export const OrgShow = ({ permissions, ...props }) => (
<Show permissions={permissions} title={<Title />} {...props}>
<SimpleShowLayout permissions={permissions}>
<TextField source="id" />
<TextField source="name" />
{
(props.record.name === 'All' && permissions === 'admin') || (props.record.name !== 'All' && permissions === 'user')
? <TextField source="bearer_token" />
: [
<FunctionField label='Bearer Token' render={record => obfuscator(record.bearer_token)} />,
<Alert severity="warning">You are not authorized for this org.</Alert>
]
}
</SimpleShowLayout>
</Show>
);
UPDATE:
I ended up solving this by using a FunctionField component, which I think is similar to user striped's solution; main difference being that FunctionField seems to be great for one-off function, whereas creating a new component seems appropriate if you're going to use that logic for more than one source. Anyway, here's the solution I came up with:
export const OrgShow = ({ permissions, ...props }) => (
<Show permissions={permissions} title={<Title />} {...props}>
<SimpleShowLayout permissions={permissions}>
<TextField source="id" />
<TextField source="name" />
<FunctionField permissions={permissions} label='Bearer Token' render={record => {
if ((record.name === 'All' && permissions === 'admin') || record.name !== 'All') {
return record.bearer_token
} else {
return obfuscator(record.bearer_token)
}
}} />
</SimpleShowLayout>
</Show>
);
I think that you need to create a specific component. The record should be automatically propagated to the child:
const MyComponent = (props) => {
return (props.record.name === 'All' && props.permissions === 'admin') || (props.record.name !== 'All' && props.permissions === 'user')
? <TextField source="bearer_token" record={props.record}/>
: [
<FunctionField label='Bearer Token' record={props.record} render={record => obfuscator(record.bearer_token)} />,
<Alert severity="warning">You are not authorized for this org.</Alert>
]
}
export const OrgShow = ({ permissions, ...props }) => (
<Show permissions={permissions} title={<Title />} {...props}>
<SimpleShowLayout permissions={permissions}>
<TextField source="id" />
<TextField source="name" />
<MyComponent permission={permissions} />
</SimpleShowLayout>
</Show>
);

Formik handleChange - use hooks

before installing Formik, my input looked like so:
const [search, setSearch] = useState('');
....
<View style={styles.profileEditContainer__top}>
<TextInput
style={styles.profileEditContainer__form}
autoCapitalize="none"
placeholder="Enter what you want to create.."
placeholderTextColor={Colors.formPlaceHolderDefault}
name="search"
type="search"
value={search}
onChangeText={(e) => setSearch(e)}
autoCorrect={false}
defaultValue={search}
/>
<Button
disabled={!search}
title="Create"
onPress={(e) => {
createNewCar(e);
}}
/>
</View>
in onChangeText, I would set every character I typed to a state prop called search. With every key that I typed, an API called would be made to get some data from the db.
for example:
if I typed h into the input, the db would return 2 cars honda, hyundai
I read that Formik can simplify a lot of the form setup in React, so I downloaded it, however, the handleChange prop from Formik wants to keep track of values.search
<Formik
initialValues={{
search,
}}
onSubmit={(values) => {
console.log('values', values);
}}>
{({ handleChange, handleSubmit, values }) => (
<View style={styles.profileEditContainer__top}>
<TextInput
style={styles.profileEditContainer__form}
autoCapitalize="none"
placeholder="Enter what you want to create.."
placeholderTextColor={Colors.formPlaceHolderDefault}
autoCorrect={false}
value={values.search}
onChangeText={(e) => {
handleChange(values.search);
setSearch(e);
}}
/>
<Button
disabled={!search}
title="Create"
onPress={handleSubmit}
/>
</View>
)}
</Formik>
Now I can't type into the form because value is pointing at values.search instead of search like it did originally.
Question
How do I fire setSearch in onChangeText but also add search into the formik values prop?
you can make use of setFieldValue('search', e.target.value) instead of handleChange() change the code to the following:
<Formik
initialValues={{
search,
}}
onSubmit={(values) => {
console.log('values', values);
}}>
{({ handleChange, handleSubmit, values, setFieldValue }) => (
<View style={styles.profileEditContainer__top}>
<TextInput
style={styles.profileEditContainer__form}
autoCapitalize="none"
placeholder="Enter what you want to create.."
placeholderTextColor={Colors.formPlaceHolderDefault}
autoCorrect={false}
value={values.search}
onChangeText={(e) => {
//handleChange(values.search);
setFieldValue('search', e.target.value)
setSearch(e);
}}
/>
<Button
disabled={!search}
title="Create"
onPress={handleSubmit}
/>
</View>
)}
</Formik>

How do you conditionally show fields in "Show" component in react-admin?

Some fields I want to only show if they have a value. I would expect to do this like so:
<Show {...props} >
<SimpleShowLayout>
{ props.record.id ? <TextField source="id" />: null }
</SimpleShowLayout>
</Show>
But that doesn't work. I can make it somewhat work by making each field a higher order component, but I wanted to do something cleaner. Here's the HOC method I have:
const exists = WrappedComponent => props => props.record[props.source] ?
<WrappedComponent {...props} />: null;
const ExistsTextField = exists(TextField);
// then in the component:
<Show {...props} >
<SimpleShowLayout>
<ExistsTextField source="id" />
</SimpleShowLayout>
</Show>
This correctly shows the value, but strips the label.
We need to update our documentation about this. In the mean time, you can find informations about how to achieve that in the upgrade guide: https://github.com/marmelab/react-admin/blob/master/UPGRADE.md#aor-dependent-input-was-removed
Here's an example:
import { ShowController, ShowView, SimpleShowLayout, TextField } from 'react-admin';
const UserShow = props => (
<ShowController {...props}>
{controllerProps =>
<ShowView {...props} {...controllerProps}>
<SimpleShowLayout>
<TextField source="username" />
{controllerProps.record && controllerProps.record.hasEmail &&
<TextField source="email" />
}
</SimpleShowLayout>
</ShowView>
}
</ShowController>
);
Maybe this way can be useful
import { FormDataConsumer } from 'react-admin'
<FormDataConsumer>
{
({ formData, ...rest}) => formData.id &&
<>
<ExistsTextField source="id" />
</>
}
</FormDataConsumer>