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

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?

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.

Related

Maintain state between Vue pages

I'm working on a form in VueJS and want the user to be able to confirm the contents before submission.
This involves routing from form page -> confirm page.
However, when I have the user go back the state of the previous page is reset.
Is there a way I can cleanly preserve the state of the previous page without digging far into the store's state?
Or is this approach against the principles of Vue?
Previous approach tried is to save the form's state when moving to the confirm screen.
Then when moving back the previous state is restored by checking if the data exists on create, e.g.
onCreated() {
this.myFormData = this.$store.state.[myFormModuleName].formData ? this.$store.state.[myFormModuleName].formData : setSomeDefaultInstead();
}
And while this works it feels hacky and is difficult to test.
This may need editing for being too broad, but is there a 'best practice' approach between Vue for maintaining state between pages as you can do in Angular? Or are plugins the only way to go?
As mentioned above, I would like to ensure that the approach used doesn't break pre-existing tests for the form.
Without having the entire code available, I think you best bet might be a <keep-alive> component around your router-view, have a look at the section in the docs here: https://v2.vuejs.org/v2/api/#keep-alive
This should preserve the entire local state of your component when it's being used again.

React Multiple Text Inputs Controlling Each Other State

Working CodeSandbox Demo
So I'm learning to use React, and trying to create a currency exchange app.
I'm having a problem understanding how to setState from multiple inputs depending on the input you're actually using.
I set up a CodePen to show you what I'm trying to do.
Things I know or don't:
Right now I'm only updating a tempValue state.
If I use the first input field it gives me the desired effect.
2.1 I know in this case I'm not updating the states, I'm just doing my math right on the value prop (but it works to show my intention).
2.2 I don't know if I should do the math on setState or on a separate method.
2.3 I can probably use a wait timer and look for onKeyDown then setState.
I could probably use uncontrolled components, but it wouldn't be the React way (is it a bad thing).
My setState originally used [computed properties] for the name but changed it for demonstration purposes.
Working CodeSandbox Demo
Since you have access to the input in your onChange function, you can get the input name off event.target and use that to make modifications in your state.
Alternatively you can bind the function:
<CurrencyInput
onChange={this.handleCurrencyInput.bind(this, currency.name)}
/>
But this isn't the best to do in your render, so you could make a renderCurrencyInput method on your class and bind once there
Computed properties would work, I don't see much of an issue with it. Then you can drive your initial this.state from your list of currencies

Vue - Force Rerendering Of All Components

I've wrote a plugin for Vue and have some issues on rerendering. In fact there is a new filter, which translate a given text by the global defined language. When the language changes, the text should be translated again. Cause there is no event queue for filters here, I want to force all components to rerender themselves, if the language changes. So the filter function would be evaluated new.
I know that I can rerender a component itself with vm.$forceUpdate(). But how can I told the whole Vue component tree to rerender? Cause this only happens, if the user change the language, the performance issues should not be a problem, cause at least the user do not have to reload the whole page and can do this offline.
Any suggestions?
You can use a key attribute in the component root. Change it and the component tree will be rerendered.
Per docs (bold is mine):
It can also be used to force replacement of an element/component
instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions
For example:
<transition>
<span :key="text">{{ text }}</span>
</transition>
When text changes, the <span> will always be replaced instead of
patched, so a transition will be triggered.

What constitutes an appropriate use of ref in React

Can someone explain how ref is used in React? I understand it's a shortcut that sort of defeats the purpose of the react DOM, but I don't know how or why exactly. I'm trying to determine whether something I'm trying to accomplish represents one of the rare cases where you should use ref
I want a custom bootstrap alert I can show from any of these pages, without using JQuery (I have one working with JQuery already)
I have a router that switches between pages, each containing a Layout component with a Page component inside (based on this)
Like:
render() {
return (<Layout ref={layout => (this.layout = layout)}>
<WhateverPage
session={this.session}
otherExampleProp={"something"}
showAlert={this.showAlert}/>
</Layout>);
}
showAlert(type, text, hasTimeout, timeoutMs) {
this.layout.alert.showAlert(type, text, hasTimeout, timeoutMs);
}
I can think of three solutions:
Use ref, which as I only partially understand defeats the purpose of react components to some extent, but I'm not sure how exactly...
Use ref, but to a lesser extent, by placing the alert component in each Layout before the Page component (so no need for a ref to ).
Create a component and a function on each page, using the page's state to control the alert, so it would be basically the same as creating a unique alert for each page, which also defeats the purpose of a component...
The example most people give when explaining what to use ref for involves focus() - is this similar? Intuitively it feels like I should use ref, but I also know that theoretically you shouldn't, but I want to understand why, because there are exceptions and for all I know this may count.
Similarly, I want to create a confirm component to replace the native JS confirm() (since it might be deprecated soon), and this approach (using ref) also makes this WAY easier than creating a component for each page, since I can pass any function as a parameter to the confirm component for it to execute on an OK button press (also gives me the option of including icons, titles, custom buttons, etc).
Existing examples and libraries all seem to use method 3 (or they're simpler, and not not really analogous).
Is ref ok to use here? Is it wrong? Why? Am I overthinking this?
Yes, you are "misusing" ref here because you're trying to build around how React is intended to be used.
ref is mainly for accessing the actual rendered DOM element - maybe to focus it, read input, get dimensions, whatever. Generally speaking though you should us ref as a "read only" feature - use it to get info about the rendered DOM but don't use it as part of a process to bypass render() or inject elements into the DOM.
What you should do is create a reusable component for your Alert. Make it flexible enough that it can accept arbitrary settings like color, text, duration, callback functions for accept/cancel/clear, etc. Then you can just render it somewhere, maybe like this:
<MyAlert
title="foo"
text="bar"
duration={5}
confirmCallback={someFunction}
cancelCallback={anotherFunction}
/>
Remember that components are a way to render and interact with state, and that's exactly what you're trying to do with your Alert. There is some kind of notification, it has content and controls for doing some action(s), and all of that should live somewhere in your app state hierarchy. There is absolutely no reason to resort to refin this situation.
To complement the jered answer:
If you plan to use the alert component in all the pages then instead of placing an alert component inside each page you can create a high order component named like PageWithAlert (I would say just Page) that includes the alert component for each page.
You might want to take a look at this https://facebook.github.io/react/docs/higher-order-components.html

ReactJS: how to update React from the DOM

I'm trying to automate a page generated by React: record the user interaction in the page and then play it back for testing purposes.
In the simplest case, the playback sets the values of various inputs and triggers change events.
Unfortunately, after my code sets the values and simulates the click on the form submit button, React steps in and resets the values to those from its internal model.
Is there a way to force React to update from the values in the DOM?
If not, is there a way for me to hook into React and change the values, given the DOM node? Something like (totally bogus pseudocode): require("react").findComponentForDomNode('xxx').setValue('yyy') ?
Make sure that your form is using "uncontrolled" components, or at the very least that you're allowing user input in your controlled components.
http://facebook.github.io/react/docs/forms.html
Alternatively, you should be able to accomplish this with React's testing utilities:
http://facebook.github.io/react/docs/test-utils.html
This lets you perform actions such as:
React.addons.TestUtils.Simulate.change(node, {target: {value: 'Hello, world'}});

Categories