Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Form controlled by a Flux Store (Best practice?)

So I have a big component that would be my form:

<form>
 <FirstComponent value={this.state.firstValue}/>
 <SecondComponent value={this.state.secondValue}/>
 {more components here}
 <input type="submit" ... />
</form>

This form component is listening for a store that updates its values using firstAction, secondAction, etc.

Note: Component updates its state based on store.getState() that returns {firstValue: something, secondValue: something, etc}

So let's say my FirstComponentis an input:

<input type="text" value={this.props.value} 
   onChange={(e)=>this.props.firstAction(e.target.value)}
</input>

Ok, so the onChange fires the prop firstAction which is actually the Flux action that will update my store and make the form to re-render. I have two good things here, when user submits the form, I can check the value of FirstComponent in my store and I also control all my state from the parent component.

However, this onChange callback will call an action every time the user types one character (so it can produce a lot of calls therefore re-renders) <-- can this provoke serious performance issues?

Instead, I could use refs and when the user press the submit button, get this.refs.myFirstComponent.state... and I will have the value too (that would be Uncontrolled Component?) But this does not sound like a recommendation from the community.

So my question is, is the first approach I described above a good way to go? How can I optimize it? So a re-render that should only affect FirstComponent does not make SecondComponent and so on to re-render? Is shouldComponentUpdatethe only way to go here?


Edit 1:

With the first approach I am facing a problem... I have an e2e test using WebdriverIO adding a value into the text field: http://webdriver.io/api/action/setValue.html

I don't know why but if I am trying to add the word "Testing" into the input, webdriver will only add the last letter. This problem is gone if not using state/store at all. However, if I have the state internally to my FirstComponent, something like:

<input type="text" value={this.state.value} 
   onChange={(e)=>this.setState({firstValue: e.target.value})}
   onBlur={()=>this.props.callback(this.state.firstValue)}
</input>

In this case, component seems to react faster while typing (only renders itself), and then, when user removes focus it updates the store. I have to say, I don't like this approach because it doesn't follow the pattern of take your state up (and I feel I am duplicating the state) BUT it seems to work faster and more important: My e2e test works. Any more ideas?

like image 485
Ferran Negre Avatar asked Jul 30 '15 23:07

Ferran Negre


1 Answers

Your first approach (i.e. onChange fires flux action which updates the store and make your form re-render) seems like a good way to go. I've been using it like that and I've seen other people using it like that, too.

Regarding your following comment:

However, this onChange callback will call an action every time the user types one character (so it can produce a lot of calls therefore re-renders) <-- can this provoke serious performance issues?

Yes, I believe so. I once created a component that contains many other components along with some input fields. Whenever I typed a character in an input field, the whole component (with other components it includes and the input fields) got re-rendered, causing the performance problem. It was noticeable if I typed fast. You can actually verify it using https://facebook.github.io/react/docs/perf.html.

Anyway, how I got around the problem is, as you mentioned, by implementing shouldComponentUpdate().

A small tip I'd like to mention is creating a custom <Input /> component that wraps around <input /> and implementing shouldComponentUpdate() (i.e. this.props.value !== nextProps.value || this.props.checked !== nextProps.checked) That way, if you create a form component, for example, with many input fields (using the custom <Input />), only the input field that is changed gets re-rendered.

I'd love to see how other people approach this problem, too, though.

like image 177
Brian Park Avatar answered Oct 22 '22 19:10

Brian Park