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'}});
Related
I have one ReactJS App which I reduced to the minimum as possible on the diagram below:
Side note: On this App I use Redux to manage state changes.
This App contains:
Component: UploadScreen with an image holder and a button. When that button is clicked, the user gets displayed a Popup Window which let him to pick an image from his device file system. Then that image is displayed on the image holder.
Component: AuxWidget which is a totally different component (needs to be separate) which also contains a button that when it is clicked it should popup the Select File window. I was thinking in something like triggering the click event of the first button.
Any idea on how to achieve that?
First I though about using Redux but I think that's not a too good idea because even though you can send messages with it from one component to another, that causes a render update and I don't want that.
Also, I was thinking on using jQuery but that's not the best approach when it comes to ReactJS.
Also, I thought about using the attribute: ref="foo" to get a reference to the other component but I think that's normally done when you want the interaction to be between parent and child components.
Also, I was thinking about EventEmmitter but I don't know if that's the best approach on this case (I'm using Redux to manage the state changes between components).
One of the best ways I can suggest using RxJS, you can create a Subject and pass it to your components. In one component you will need to subscribe to it and whenever you will call next on your subject from the second component, the other will be notified, so you can trigger open popup. You can even create your own implementation for this in case you don't want to add new library to your project.
The upload window could be triggered when a certain state in the app changes. The relevant state on the app could be changed from different places, like from AuxWidget and UploadScreen. That way they are not coupled with the upload window. They merely call a function that is passed to them and that function changes the state on the app and it will display the window.
If you have a shared component between two unrelated component I think it is best to lift that common component and let its state sit on a higher level.
If I understand things correctly, your primary concern is code-reuse as opposed to wanting to call a sibling method. Basically, you want a SelectFilePopup component that can be re-used (open/closed) cleanly. I think React Portals could be a good solution for this. I found a good example (https://github.com/Assortment/react-modal-component/blob/master/src/components/Modal.js) of how a Modal can be isolated into a component and be called anywhere in the codebase.
The usage of the Modal looks like this (copied and slightly modified from App.js in the github project above)
import Modal from './components/Modal';
<Modal><div>Click me to open Modal</div></Modal>
And the Modal component implementation (simplified)
render() {
return (
<Fragment>
<ModalTrigger
onOpen={this.onOpen}
/>
{isOpen &&
<ModalContent/>
}
</Fragment>
)
}
By default the Modal component shows a trigger (i.e button) when isOpen state is false. Once clicked, and isOpen switches to true, the ModalContent (i.e can be the FilePickerPopup) is dynamically created and attached to document body. You can check out the source code for more details. I think its a very clean solution to modals. So in your case, your code could end up looking something like this
UploadScreen.js
import FileSelectPopup from './components/FileSelectPopup';
<FileSelectPopup>{Upload Image}</FileSelectPopup>
AuxWidget.js
import FileSelectPopup from './components/FileSelectPopup';
<FileSelectPopup>{Upload Image or some other text}</FileSelectPopup>
So basically, AuxWidget doesn't even need to know about where the FileSelectPopup is located at. It's an independent component that can be called anywhere. The caveat is that the Modal implementation in the project I linked to is not a singleton (although it can be modified to be one). So if AuxWidget and UploadScreen are visible to the user at the same time, clicking both Upload Image buttons will create two instances of the Popup.
I would define the function in the parent component and pass it to both children as props
In suite of React apps we're placing per view set of reusable apollo-backed form components with just one save button placed outside of the form components. On a save button clicked each component (with dirty state) should execute the mutation to persist changes.
I wonder about possible implementation options and I would like to avoid using refs.
The problem to be solved seems to be - how to call a method outside of the component - I tried to follow on this question Call child method from parent and while I'd rather do not use any React way of communication between components, one particular answer that looks promising to me is https://stackoverflow.com/a/45582558/3021889 - still I'd like to hear what options do I have.
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
I found this question that is similar, but it did not explain the issue I am trying to solve for. (Using Vue.js 2)
I have an array of objects (users), that I display as a list.
I want to open an "edit user" modal component, which needs to prefill the form values with the user data (exact parent object), but I do not want to edit the parent data until I click a save button that verifies the data before emitting an input event with the new values of the object.
I do not want to manually call out each property to create a new "clean" object (if I change the data type this is a headache), but Vue is adding its reactivity to the user object and therefore modifying the value in the component updates the parent value at the same time.
I also want to wait for the server to confirm a change is saved before emitting the input event; therefore the parent won't update until the data is confirmed on the server.
Is there a recommended pattern for non-reactively modifying props in components, instead using the emitted input event to update, as per the Vue component prop/event specs?
Note: I already have a working example, save for the concern with separating the reactivity bit.
I've an HTML table generated by React render() which is tied to a websocket for realtime updates.
What I'm looking to do is attach a React event handler "onClick" to the cell which replaces the value in the cell with a custom piece of DOM such as some Bootstraps dropdown HTML so the user can update that cell.
I can use .getDOMNode() in the click event handler and then manipulate the DOM manually and insert the code, however if render() on this component was called due to a new updated state from a websocket event it would be overridden. If this race condition occurs, I need to inform the user instead of the DOM simply being replaced. I've just seen componentWillUpdate() though this still feels like I'm not using React correctly.
Is there a better approach ? It feels dirty to be manipulating the DOM and incorrect to change the state.
Updating internal component state is there for exactly this case. This component should be in charge of determining whether it is active or not. If you update state on the component when the click handler is triggered with something like active: true, then you trigger a re-render. In your render function add the additional DOM elements if active is true. Then you have three different possibilities that should be accounted for:
onBlur
onUpdate which can
be triggered in two ways. either way you'll likely want to set
active: false on state.
websockets - you will need to inform your
user here probably regardless of if the component is active or not
user - which will just update normally
Doing it this way means that React is always in full control of the DOM, which is a really good way to avoid issues with React.