I have been trying to figure out the best way to manage my react forms. I have tried to use the onChange to fire an action and update my redux store with my form data. I have also tried creating local state and when my form gets submitted I trigger and action and update the redux store.
How should i manage my controlled input state?
I like this answer from one of the Redux co-authors: https://github.com/reactjs/redux/issues/1287
That is, if you're sure that your form won't affect global state or need to be kept after your component is unmounted, then keep in the react state.
TL;DR
It's fine to use whatever as it seems fit to your app (Source: Redux docs)
These questions can easily help you identify the approach that would be a better fit for your app. Here are my views and approaches I use in my apps (for forms):
Local state
input
(s) and submits. I use this most of the time for simple forms.Redux state
input
(s) causes some other components torender
depending on what is being input by the user.You can use the component's own state. And then take that state and give it as an argument to the action. That's pretty much the "React way" as described in the React Docs.
You can also check out Redux Form. It does basically what you described and links the form inputs with Redux State.
The first way basically implies that you're doing everything manually - maximum control and maximum boilerplate. The second way means that you're letting the higher order component do all the work for you. And then there is everything in between. There are multiple packages that I've seen that simplify a specific aspect of form management:
React Forms - It provides a bunch of helper components to make form rendering and validation more simple.
React JSON schema - Allows one to build an HTML form from a JSON schema.
Formsy React - As the description says: "This extension to React JS aims to be that "sweet spot" between flexibility and reusability."
Update: seems these days Redux Form is being replaced with:
And one more important contender in the space that's worth checking out is:
Using the helper libraries is just more quick and avoid us all the boilerplate. They may be optimized, feature rich ...etc. As they make all different aspects more of a breeze. Testing them and making your arsenal as knowing what's useful and better for the different needs, is just the thing to do.
But if you already implemented everything yourself. Going the controlled way. And for a reason you need redux. In one of my projects. I needed to maintain the form states. So if i go to another page and come back it will stay in the same state. You only need redux, if it's a mean for communicating the change to multiple components. Or if it's a mean to store the state, that you need to restore.
You need redux, if the state need to be global. Otherwise you don't need it. For that matter you can check this great article here for a deep dive.
One of the problems that you may encounter! When using the controlled inputs. Is that you may just dispatch the changements at every keystroke. And your form will just start freezing. It became a snail.
You should never directly dispatch and use the redux flux, at every changement. What you can do, is to have the inputs state stored on the component local state. And update using
setState()
. Once the state change, you set a timer with a delay. It get canceled at every keystroke. the last keystroke will be followed by the dispatching action after the specified delay. (a good delay may be 500ms).(Know that
setState
, by default handle the multiple successive keystroke effectively. Otherwise we would have used the same technique as mentioned above (as we do in vanilla js) [but here we will rely onsetState
])Here an example bellow:
You can use the anti-pattern for initializing the component using the props as follow:
In the constructor or in the
componentDidMount
hook like bellow:The later allow us to restore the state from the store, at every component mounting.
Also if you need to change the form from a parent component, you can expose a function to that parent. By setting for
setInputs()
method that is binded. And in the construction, you execute the props (that is a getter method)getSetInputs()
. (A useful case is when you want to reset the forms at some conditions or states).To understand better what i did above, here how i'm updating the inputs:
and here is my form:
Personally, I highly recommend keeping everything in the Redux state and going away from local component state. This is essentially because if you start looking at ui as a function of state you can do complete browserless testing and you can take advantage of keeping a reference of full state history (as in, what was in their inputs, what dialogs were open, etc, when a bug hit - not what was their state from the beginning of time) for the user for debugging purposes. Related tweet from the realm of clojure
edited to add: this is where we and our sister company are moving in terms of our production applications and how we handle redux/state/ui