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 FirstComponent
is 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 shouldComponentUpdate
the 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?
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.
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