Taming the React Admin Dependency Fields 
Tags: react, javascript, aws, serverless
Published: 2019-12-03 15:10:52

Today ticket was to add an (owner name and email fields) in one of our tools.

those owners will come from Cognito-pool that exist within the backend. aside from this a default value should be used and this one comes from the already signed-in User(owner) on the front-end.

I know it sound like hHaaaa

![what...?](https://media.giphy.com/media/lqpV8eN9GcF2McoFJP/giphy.gif)


This tool allows you to create a campaign that is used by our marketing team, where they set campaign that holds infos like expanses partners and a lot of other infos.

The campaign was built using react-admin platform
https://marmelab.com/react-admin/

Which is a great open source frontend Framework for building admin applications running in the browser, on top of REST/GraphQL APIs, using ES6, React and Material Design. 

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/vlo0ykki80aeouov6ftd.jpg)
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/lmsdikkyvuhr2fxajnl9.jpg)

The only problem that I usually face with this platform is the BIG documentation and it's most of the times not clear to me where to start and how a specific component really works, what is the params and how it its working under the hood, but after a while I start liking this platform.

Basically the more you use it the more you will realise its fitting and has almost everything you need.

Back to the main task: so i need it to create 2 fields owner name and email and the email should be set automatically once you select the owner name and not to forget the default values incase there was no data or incase of creating new campaign.

In our case the owners was coming from the cognito-pool and not from a database

So I implemented a REST API called list-owners where this one use a lambda function on the backend that reads the cognito pool and return a list of users 
As the following:
 
```

[
   {
       id: '123',
       name: 'Ahmed Zeino',
       email: 'azeino@company.com'
   },
   {
       id: '124',
       name: 'John doh',
       email: 'jdoh@company.com'
   }
]

```


Now the front end ,

React admin allows you to use a lot of viewing fields components  such as 
<AutocompleteInput>
<ArrayField>
<TextField>

An example of the usages if AutocompleteInput is like this:-

const choices = [
    { id: 123, owner_name: 'Ahmed Zeino', email: 'azeino@company.com' },
    { id: 124, owner_name: 'John Doh', email: 'JDoh@company.com ' },
];
<AutocompleteInput source="id" choices={choices} optionText="owner_name" optionValue="id" />

All of these components have some common properties like the source and reference.

The reference is the path that the api will look for and in our example its /owners as we set this in the rest api.
	This will do the request and return the response that has the array of owners

```
[
   {
       id: '123',
       name: 'Ahmed Zeino',
       email: 'azeino@company.com'
   },
   {
       id: '124',
       name: 'John doh',
       email: 'jdoh@company.com'
   }
]
```

The react-admin component will use this as a source of data (choices in the AutocompleteInput component ) 

React admin will inject this data directly with component , without the need to use choices
And the component should display nicely the owners name list.

But the problem lies in the way that you need to display the email on a side field  based on the selected owner name , even if there is no relation between those two.


So what to do ?

I solved this by splitting the problem into 3 parts

1. I need to display a list of owner names and a TextField that display the email.

2. I need to be able to pass the data coming from the api /owners to those two components.

3. I should be able to set the default values to these  2 components incase of the first load and in case there is no data yet.

so i did the following :-

I created a custom components called OwnersInput , this component will do the query of the rest api and pass the data to the sub components 

<AutocompleteInput/>  and <TextInput/>  

I wrapped the <TextInput/>  with a <FormDataConsumer/> thats allowing me to check the current form fields and allowing me to dispatch a change event that will change specific field of the form.


I'll put the component here and write more explanations on it

```
export const OwnersInput = ({
   label,
   source,
   reference,
   optionValue,
   user = {},
   formName = ''
}) => {
   const {name: defaultUser} = user
   const {email: defaultEmail} = user

   return (<Query type={GET_LIST} resource={reference} payload={ownersPayload}>
           {({data}) => {
               return (data && (<div>
                           <AutocompleteInput
                               translateChoice={false}
                               choices={data}
                               allowEmpty
                               label={label}
                               optionValue={optionValue}
                               source={source}
                               validate={validateOwner}
                               defaultValue={defaultUser}
                           />
                           <FormDataConsumer>
                               {({formData, dispatch}) => {
                                   const email = getEmail(
                                       formData,
                                       data,
                                       defaultEmail
                                   )
                                   if (
                                       formData &&
                                       formData.owner_email !== email
                                   ) {
                                       console.log('CHANGING EMAIL')
                                       dispatch(
                                           change(
                                               formName,
                                               'owner_email',
                                               email
                                           )
                                       )
                                   }
                                   return (
                                       <TextInput
                                           source="owner_email"
                                           label="email"
                                           defaultValue={defaultEmail}
                                           disabled
                                           style={layout}
                                       />
                                   )
                               }}
                           </FormDataConsumer>
                       <div/>)
               )
           }}
       </Query>
   )
}
```


When creating this component i am giving it the init user name and email which is already saved in the state - on the front side (based on logged user) and thats how i am creating the default name and default email

I am also passing the reference as reference="owners" 
And also the formName ,so i can do the dispatch on the right form
The component will do the query using GET_LIST
For the initial value u can pass it  as defaultValue={defaultUser} inside the component
The get email function will check for the form field owner_name and it will grab the needed email after it compare it with the data , if not it will return the default email



Here is the usages inside a create page:-
```
<OwnersSingleInput
                    reference="owners"
                    label="owner"
                    optionValue="name"
                    source="owner_name"
                    formName="createForm"
                    user={user} // injected from the state
                />
```

I hope it was a clear and simple explanation ,otherwise feel free to ask me.


just run `npm run myscript`