Determine method of State for report generation form (i.e. Local State vs redux state)
***Note:*** Code used in this document are only pseudo and not tested code. basically its just a concept to show the complexity/ease of implementation.
**Section 1**: Goal to find out which option would be feasible to use between React form hook library vs Redux and controlled component.
To determine which option would make more sense to use for change history, I tried to answer these 5 questions based on my understanding of the requirement. After discussing this with fellow developers, here is the jist output:
1. Do other parts of the application care about this data?
=> No, totally independent to other parts of caseflow
2. Do you need to be able to create further derived data based on this original data?
=> I don't think we need this.
3. Is the same data being used to drive multiple components?
=> somewhat but not fully.
4. Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
=> Don't see much use of time-travel debugging in this filter form as we should be able to use the local state in dev tools.
5. Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?
=> not really.
**Local state**
Local states are useful when any forms has no relation with other components and is used to just capture data and generate result or submit.
Mostly used when time-travel debugging is not needed.
Pros of using react-hook-forms:
1. Isolate Re-renders
2. Easy to implement
3. Easy implmentation of Validation using Yup
4. Controlled mixed with uncontrolled components
5. Predictable dataflow
Cons of using react-hook-forms:
1. Uses react hooks
**Redux state**
Useful mostly when the form/components has to update some other components.
Useful when form inputs causes some other components to render.
Pros of using redux-components:
1. Controlled components
2. centralized state management
3. Predictable data flow
Cons of using redux:
1. Performance Overhead
2. BoilerPlate code
3. Complexity
**Section 2:** To figure out how to:
**2a) Able to reset form externally**
Both react-hook-forms and redux-controlled component have ability to reset form externally.
**With react-hook-forms** we set default values and use reset api to reset externally.
example of default form values and how it can be used to reset
```
const defaultFormValues = {
"report_type": {"status"},
"status": null,
"days_waiting": null,
"facility": null,
"decision_review_type": null,
"issue_type": null,
"issue_disposition": null,
}
function App() {
const { handleSubmit, reset, watch, control, register } = useForm({
defaultValues,
})
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
render={({ field }) => (
<Select {...field}>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
</Select>
)}
control={control}
name="select"
defaultValue={10}
/>
<Input {...register("input")} />
<button type="button" onClick={() => reset({ defaultValues })}>
Reset
</button>
<input type="submit" />
</form>
)
}
```
**With Redux Component** we will have to use the normal flow or boiler plate codes to reset the value to original set of data.
```
**Actions**
export const clearAllFilters = () => (dispatch) => {
dispatch({
type: Constants.CLEAR_ALL_FILTERS,
...
});
dispatch(somefunction());
};
**code in reducer **
case Constants.CLEAR_ALL_FILTERS:
return update(state, {
FilterCriteria: {
category: {
$set: {}
},
tag: {
$set: {}
}
},
});
**Call from the component might be something like this**
return <Button
id="clear-filters"
name="clear-filters"
classNames={['cf-btn-link']}
onClick={props.clearAllFilters}>
Clear filters.</Button>;
}
```
**2b) Dynamic Options(Users)**
Getting dynamic options for any dropdown should be easy to implement using both Redux state and local hook state. for both we need to determine how we are populating dropdown options. We can use json file, or APIUtil or props to get data from the backend but we will have to write custom code to filter/populate the dropdown as needed.
"we will always be making an API call to get the options for that filter. for facilities we’ll be using a JSON file to get the values."
One benefit of using RHF here would be RHF has a method called [watch](https://www.react-hook-form.com/api/useform/watch/)/[useWatch](https://www.react-hook-form.com/api/usewatch/) which can be used to determine what to do render by condition and isolately re-render at the custom hook
Craig's Commnet-> "my biggest concern about using RHF is that for the filter section, there is always at least one controlled component (the dropdown box to select what filter type). it looks like this is simple to handle with the <Controller> component in that library though so it might not be an issue."
example use ->
https://codesandbox.io/s/react-hook-form-watch-v7-qbxd7?file=/src/index.js:0-1756
https://codesandbox.io/s/react-hook-form-v7-usewatch-forked-9872t
```
*** React hook form ***
code copied from: https://codesandbox.io/s/dropdown-react-p0nj7?file=/src/App.js:1574-1586
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [cities, setCities] = useState([]);
const [selectedCounty, setSelectedCountry] = useState("");
const [selectedCity, setSelectedCity] = useState("");
const countries = {
France: ["Paris", "Marseille", "Lille", "Lyon"],
Usa: ["New York", "San Francisco", "Austin", "Dallas"],
Brazil: ["São Paulo", "Rio de Janeiro", "Salvador"]
};
const countryList = Object.keys(countries).map(key => ({
name: key
}));
function handleCountrySelect(e) {
const countrySel = e.target.value;
const citiesSel = countrySel !== "" ? countries[countrySel] : "";
setSelectedCountry(countrySel);
setCities(citiesSel);
setSelectedCity("");
}
function handleCitySelect(e) {
console.log("Selected city", e.target.value);
const citiesSel = e.target.value;
setSelectedCity(citiesSel);
}
return (
<div className="App">
<h1>Example Dynamic Dropdown</h1>
<div className="Container">
<select
name="Countries"
onChange={e => handleCountrySelect(e)}
value={selectedCounty}
>
<option value="">Select the country</option>
{countryList.map((country, key) => (
<option key={key} value={country.name}>
{country.name}
</option>
))}
</select>
<select
name="Cities"
onChange={e => handleCitySelect(e)}
value={selectedCity}
>
<option value="">Select the city</option>
{cities.map((city, key) => (
<option key={key} value={city}>
{city}
</option>
))}
</select>
</div>
</div>
);
}
***redux ***
// this can be made dynamic with the params th
getVhaAdmin = () =>{
ApiUtil.get('/users?role=admin&organization=vha').then((resp) => {
const userOptions = _.values(ApiUtil.convertToCamelCase(resp.body.users)).map((admin) => ({
label: admin.fullName,
value: admin.id # user id
}));
this.props.dropdownData( userOptions);
});
}
const mapDispatchToProps = (dispatch) => bindActionCreators({
dropdownData
}, dispatch);
#Actions
export const onReceiveDropdownData = (data) => ({
type: ACTIONS.DROPDOWN_DATA,
payload: {
data,
},
});
#reducers
case ACTIONS.DROPDOWN_DATA:
return update(state, {
[action.payload.dropdownName]: {
$set: {
options: null,
isFetching: true,
errorMsg: null
}
}
});
```
**2c) Able to structure the form submission data for request body.**
Both has capability to structure the data format for sending request.RHF has an inbuilt method called '[register](https://www.react-hook-form.com/api/useform/register/)' which helps to apply validation rules as well as format the in submit result.
for example:
```
Input Name Submit Result
register("name.firstName") {name: { firstName: 'value' }}
register("name.firstName.0") {name: { firstName: [ 'value' ] }}
```
Sample Structure for form submission:
```
const onSubmit = (formData) => {
const searchQuery = {
"status": {"Incomplete", "In Progress", "completed"},
"days_waiting": [{}],
"facility": {},
"decision_review_type":{},
"issue_type":{},
"issue_disposition":{ }
};
ApiUtil.post('/decision_review/generate_reports', { data: searchQuery }).
then((response) => {
setResponseActions();
}).
catch((error) => {
setErrors();
});
};
```
I think the concept of both RHF and Redux form (controlled state) is to make development simple and easy but due to some core factors such as filter data not necessarily impacting other part of application, ease of implementation, not caring much about caching or driving multiple components and pros discussed above I think using RHF would be a better path to go.
**Craig's Suggestion:**
"the other potential option i suppose is that we could use local state and build the form ourselves, instead of using RHF, but i don’t know if that would give us any advantage. as far as the form goes, there isn’t going to be any custom validation needed since we are providing the users with the options that they can select, there won’t be any free text entries. at most we would need to specify required for filter options."