I have a parent component which is a form and it is connected to the store via redux by doing so:
// FormComponent
export default connect(mapStateToProps, mapDispatchToProps)(FormComponent);
Inside that form, I have a list of input attributes which are children components of a form:
I have a requirement to save changes in any field to the server by making update API call by passing JSON object that contains all attributes as soon as the input loses focus.
the API call would be something like:
updatePersonInfo({firstname: 'new name', lastname: 'new name', address: 'new address' });
My idea is to pass PersonInfo
object as props from Form component to all children component. Each child component will update one attribute from PersonInfo
props and dispatch UPDATE_PERSONINFO action with updated PersonInfo
object.
But to do that, I need to connect all child component to store also:
// FirstNameComponent
export default connect(mapStateToProps, mapDispatchToProps(FirstNameComponent);
// LastNameComponent
export default connect(mapStateToProps, mapDispatchToProps)(LastNameComponent);
// AddressComponent
export default connect(mapStateToProps, mapDispatchToProps)(AddressComponent);
But my thinking that we should avoid using connect for performance reason. Also, I'm not convinced that I need 4 connectors to call one action.
What would be the best option to handle this scenario?
We specifically recommend that most form state probably shouldn't be kept in Redux. However, it's your app, so you should evaluate the specific use cases you need to deal with to determine the right approach.
For this problem I can offer a good trick that I learned a while ago, in these situations we should use container component's state for creating a controlled form component and update the state on every form change, by that you can always access your form data from state of the container component. (For example: because of implementing controlled component, we can always send form data that is available for us from container component's state to Redux by a save button or in situations like your case Focusing on another field).
So let's get started:
(1) you need to use a name
attribute and an onChange
for each input, like so:
<input
type='text'
name={name}
className='form-control'
value={value}
onChange={onChange}/>
(2) the onChange
function is placed in your container component and it is passed down via props to your input component and looks like this:
updateCourseState(e) {
const field = e.target.name
const _formData = {...this.state.formData}
_formData[field] = e.target.value // (*)
return this.setState({ formData: _formData}) // <= then update the state on every change
}
In here (*) by using precomputed dynamic variable names, we create a property for each input field in our temp _formData object and assign the target value of event that is been passed up, to it, after that we update the state.
(3) and finally you can now pass the state to redux when every you want, and as I read through your question you can use Refs()
for that to keep track of when use focuses on another field you can check this tutorial on YouTube for that and when you change the focus, here is the place that you want to send your data to Redux.
You can make the API call in each of the component directly to update only the corresponding attribute, so that you can avoid Redux for this.
Eg. updatePersonInfo({ firstname: 'new name' })
.
Your API endpoint should be a PATCH
method that updates only the fields that are in the payload (in the above example, it should only modify the field corresponding to firstname
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With