Submit button from outside Redux Form v6.0.0 - javascript

This question relates to Redux Form v6.0.0 (in time of writing this question it is v6.0.0-alpha.15).
How can I get form validation status (like pristine, submitting, invalid) from outside of form component ?
Let me give an example. This is "classical redux-form" pseudo-structure:
<Form(MyExampleForm)>
<MyExampleForm>
<input name="title" ... />
<submit-button />
...where <submit-button> in JSX looks like this:
<button type="submit" disabled={pristine || submitting || invalid} >Save</button>
But in my application, my submit button must be outside of the form, placed on different place in the application (let's say in application header, on the top of whole application).
How can I get those pristine, submitting, invalid from outside of Redux-Form? (Without really nasty hacking, if possible :-))
How can I submit that form?

Just decorate another component with same form name and you have access to same state variables there. Also you can pass it onSubmit function from parent and be able to submit all the Field values from wherever you define them as they are all from redux state, not HTML of current form instance. (it is kind of "hacky" way, but it feels right)
The submit function is defined from parent, not shared in state, so you can have it different for every instance.
class MySubmitForm extends React.Component {
render() {
return (
<button
onClick={this.props.handleSubmit}
>
{this.props.pristine ? 'pristine' : 'changed'}
</button>
)
}
}
export default reduxForm({
form: 'myFormName'
})(MySubmitForm);

redux-form works with React Redux to enable an html form in React to use Redux to store all of its state.
If "outside of Redux-Form" means still redux application, you can try to store those properties in state by dispatching some actions.
In forms: you're detecting whats happening (when its invalid etc), dispatch an action to modify a state,
In "outside" part: you're passing a proper property to component (with those you need) and depends on that you disable a button.

in latest redux-form version 6.0.2:
it is possible to access form state pristine, submitting, invalid via selectors http://redux-form.com/6.0.2/docs/api/Selectors.md/
it is possible to export redux-form action creators http://redux-form.com/6.0.2/docs/api/ActionCreators.md/

Maybe you can have a look at the Instance API of redux-forms. It provides access to a submit() method on an instance of your decorated form component. There is also a pristine Boolean property and an invalid Boolean property available (there is a request to expose the submitting property too).
There is an example here : http://redux-form.com/5.3.1/#/examples/submit-from-parent?_k=jgv0m4 (example is for 5.3.1, but the process is similar with v6 using the Instance API)
The basic idea is that by adding a ref="myExampleForm" to your form, you can pass it around with this.refs.myExampleForm. You can then check properties of the instance or call the submit() method (or any other method exposed).

Now is easier to do this. You can call the Submit action in the standalone component that has the button.
See this example:
https://redux-form.com/7.1.2/examples/remotesubmit/

If we are talking about just submitting the form, then you should provide {withRef: true} option to your redux connect() function.
Consider Row component that has child RowDetail component which has information that should be saved from Row.
RowDetail in this case could be created like this:
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';
const RowDetailForm = reduxForm({form: 'row-detail-form'})(RowDetail);
export default connect(mapStateToProps, null, null, {withRef: true})(RowDetailForm);
Then, in your parent component (Row) you create your form with ref attribute:
<RowDetailForm ref={'rowDetailForm'} .../>
Submitting now is 'easy':
onSave() {
this.refs.rowDetailForm.getWrappedInstance().submit();
}
If we are talking about pristine and other form properties, then you could try to get them from your mapStateToProps function in your parent component.
const rowDetailFormName = 'row-detail-form';
const mapStateToProps = (state) => ({
rowDetailForm: state.form[rowDetailFormName]
});
But this way seems a bit hacky because as I understand redux-form API all form states were never meant to be accessed directly. Please correct me if I am wrong

I had to tackle this issue recently. I ended up passing a callback to the form, which is invoked every time the properties I'm interested in change.
I use the componentDidUpdate life cycle method to detect changes.
This should work on any version of the library.
Here is a post with sample code - http://nikgrozev.com/2018/06/08/redux-form-with-external-submit-button/

Related

Need some suggestions for a dynamic form using React

I'm building an enterprise-level application and I need some tips and suggestions for handling dynamic form.
The fields of the form are totally dynamic and they come differently for each user.
I loop through each field(fields come from an API call) on a file called renderUiType.js and based on a property of the field called uitype, we render different Inputs.
For example if uitype===1{render TextField}, if uitype===2{ render Checkbox } and so on...
So far the displaying part is correct but now I want to save the values of each field rendered and have them all in an object so I can do a POST API Call
So my question is, how can I do that? Should I create an onChange handler function for each form-element at the main file renderUiType.js and then pass it with props to the form-elements components or should I use Redux?
Any suggestion/article or anything is welcomed. Thank you
The folder structure looks like the image below(just in case it helps to understand what I ask)
..
You can use one callback function and use it in each onChange component specific handlers. You could have everything in state of the Form if you would like hidden under the unique keys/id, so you don't need to have Redux. f.e.
if (uitype===1)
{render <TextField value={this.state[fieldId]} onChange={this.onChange}/>}
if (uitype===2)
{ render <Checkbox value={this.state[fieldId]} onChange={this.onChange}/>}
or to simplify:
const getComponentByUIType = (uiType) => {
switch(uiType) {
case 1: return TextField
case 2: return Checkbox
}
}
// ...
onChange = fieldId => value => this.setState(state => ({fieldId: value}))
//...
render() {
getComponentByUIType(uiType).map(Component => <Component value={this.state[fieldId]} onChange = {this.onChange(fieldId)} />
}
Using Redux for this shouldn't be necessary unless you need to access this form's state somewhere outside the form. If you only need the form info to do a POST, I would keep all the data inside one component's state.
Just use the unique ID provided by the IP (the one you were gonna use for the POST) to build that state object. Every field will have an onChange that updates the main form component's state, and then that same value from the state is passed in to each field as a prop.

What is the best practices to work with forms in ReactJS?

In VanilaJs we take data from form during onSubmit, using FormData make request. In ReactJ documentation we see that before submit it's recommended to setState data which introduced user.After onClick on submit button we take this data from the state and make request. So is it best way to work with forms using React?
You can do the same as your old approach here as well, but the problem is that your are delaying form submission since HTML Collections take more time to iterate than looking up and managing states. Look at the example:
import React from 'react';
class Login extends React.Component {
login(event) {
event.preventDefault();
const data = {};
const inputs = event.getElementsByTagName('input');
for (let input of inputs) data[input.id] = input.value;
// send `data` to server to test for login
console.log(data);
}
render() {
return (
<form onSubmit={this.login}>
<input id="email" type="email" />
<input id="password" type="password" />
<button type="submit">Login</button>
</form>
);
}
}
Just imagine the time taken to fetch from the DOM and then iterating each element, versus managing state on each keypress during form submission.
Depending on the size of your project, there have been a lots of packages that handle form submission easy, e.g: formik, redux-form, react-final-form, to name few. But most of these use the same technic. For example formik does right just that and also handles some edge cases for you so that you don't have to worry about.
Briefly, I would say yes, that's the best way or at least the react way, to handle form data.
They are multiple way of doing form in react, and many doesn't involve the state.
Many people use redux for storing their application states.
You can, of course, use your component state, but you can also use redux-form (popular) or formik (lightweight).
Storing into the state is as easy as storing into an object, but then it can become complicated to store everything in one place in your React DOM..
This is why redux exist and why you should read what it is, it will help your to get the answer.

Why is ugly hack required for react-redux to re-render redux-form?

I have a react app that is using a redux store and this component also uses redux-form. I am seeing a strange behavior on one particular type of update where I want to trigger a follow-on update.
Basically a user is updating permissions for something on the form, then saves. The save submits the form and in the processing of the action, it triggers another action (getting a list of all the somethings). When the list is returned from the server, this dispatches an action to update the list UI. At this point I am updating a property in the redux store for the form to say trigger an update of the permissions for the currently editing thing. This is the only point where I can do this because we now have the information to trigger this request.
The problem is that a boolean flag is being set as the trigger, and the mapStateToProps is getting called with the updated flag, but the component is not subsequently getting called (shouldComponentUpdate and componentWillUpdate never get called). It is like it ignores the state of the boolean variable in the props.
I found that if, in mapStateToProps I set another property based on the
boolean trigger, then the component is updated (re-rendered).
function mapStateToProps(store, ownProps) {
...
let triggerFetchSomethingOwnershipHash = 0;
if (store.triggerFetchSomethingOwnership) {
triggerFetchSomethingOwnershipHash = 100000 * Math.random();
}
return {
...
triggerFetchSomethingOwnership,
triggerFetchSomethingOwnershipHash
}
}
...
const ConnectedSomethingDetail = connect(mapStateToProps)(SomethingForm);
export default ConnectedSomethingDetail;
Why do I need to do this and how can I avoid it?
Yes I have checked a few relevant SO questions like react-redux-component-does-not-rerender-on-store-state-change and react-redux-update-item-in-array-doesnt-re-render

What's the correct way of accessing input fields in React-Redux?

So I have this application which uses Redux to store some data. Then I have the Search component, which I originally wanted to be a plain dumb component. However, there is an input field whose value I would like to access and pass on to do something with it. However, I am not sure how to manage the value of this input field, that is, how to access it & where to store the value. I want to use Redux and be consistent, so that's my main problem. I have found the following solution (which works), but this does not look like Redux to me anymore? Am I violating any Redux specific rules with this?
On the other hand, how would I do it with Redux? With a reducer & action etc. just for this one input field in one component? This seems like a bit too much, but please enlighten me!
class Search extends React.Component{
constructor(props) {
super(props);
this.state = {
username: ""
};
this.handleUsernameChange = this.handleUsernameChange.bind(this);
}
handleUsernameChange(evt) {
console.log("Helo" + evt.target.value);
this.setState({
username: evt.target.value
});
}
onSubmit(e) {
e.preventDefault();
console.log("Hello" + e);
/* Do something... */
}
render() {
// Return JSX via render()
return (
<div className="">
<h1>Youtube Link</h1>
<input className="form-control" onChange={this.handleUsernameChange}></input>
<button className="btn btn-large btn-positive" onClick={this.onSubmit}>Download</button>
</div>
);
}
}
// Export Search
export default Search
"I want to use Redux and be consistent, so that's my main problem."
That's a valid reason to use Redux for this use case. But it is also fine to use combination of Redux and local component state in your application. I think this is a perfect example on when local component state is a good solution. I wrote a blog post on this topic. If you want, you can take a look here: http://blog.jakoblind.no/2017/02/08/is-using-a-mix-of-redux-state-and-react-local-component-state-ok/
On the other hand, how would I do it with Redux? With a reducer & action etc. just for this one input field in one component? This seems like a bit too much, but please enlighten me!
Yes, this is how you would do it with Redux. If you need the the username value anywhere else in your app, then this is the approach you should take.
If you don't want to write your own actions and reducer and everything for it, you could look into redux-form to handle it for you, but it might be a bit overkill if there is not much other input required from the user.
If you only need it when you hit that button right below it, then what you have is fine as you can just raise your action with the value from the components state, e.g,
onSubmit(e) {
e.preventDefault();
console.log("Hello" + e);
/* Do something... */
this.props.downloadTheThing(this.state.username)
}
where downloadTheThing is mapped in your mapDispatchToProps function when connecting the component (assuming you are using react-redux).
It is definitely fine to use local state in Redux.
From the code snippet you shared, you don't even need to use a local state.
constructor(props) {
...
this.username = '';
...
}
onSubmit() {
console.log('Hello ' + this.username);
}
render() {
return (
...
<input type="text" onChange={e => this.username = e.target.value} />
...
);
}
<input className="form-control" onChange={this.handleUsernameChange}></input>
that is your input field. first thing todo when you work on input in react is to set the value and then apply onChange. with value we will assign the state of the input to the state of the component so we will get single source of truth.
since you want to work on redux store, I assume you already have your redux store, reducers and actions set in your application. reducers hold the state and you send message to reducers via dispatch(). You access to dispatch() on this.props.dispatch() once you define connect() (). By default, a connected component receives props.dispatch()
import React from "react"; //anywhere you are using jsx, u should import this
import { connect } from "react-redux";
import setInputAction from "relative path" //actions are the messages that you send to reducers. reducers change the state based on the actions.
//this allows you access to state in Redux-store via the props.
const mapStateToProps = state => {
return {
reduxState: state
};
};
export default connect(mapStateToProps)(Search);
this code is kinda configuration of your component to communicate with the redux-store.
Now let's focus on input field. we have to set the value:
<input
type="text"
value={this.props.reduxState.input}// i assume you have `input:""` in the state. u can name anything you want.
onChange={e => {
this.props.dispatch(setInputAction(e.target.value));//we are sending e.target.value to the reducer via dispatch().
}}
/>
setInputAction is just an object that sent to reducer. Reducers are already defined what to do under certain actions. So you just send the message and reducer will change the state accordingly.

Call methods on React children components

I want to write a Form component that can export a method to validate its children. Unfortunately a Form does not "see" any methods on its children.
Here is how I define a potential children of Form:
var Input = React.createClass({
validate: function() {
...
},
});
And here is how I define Form class:
var Form = React.createClass({
isValid: function() {
var valid = true;
this.props.children.forEach(function(component) {
// --> This iterates over all children that I pass
if (typeof component.validate === 'function') {
// --> code never reaches this point
component.validate();
valid = valid && component.isValid();
}
});
return valid;
}
});
I noticed that I can call a method on a child component using refs, but I cannot call a method via props.children.
Is there a reason for this React behaviour?
How can I fix this?
The technical reason is that at the time you try to access the child component, they do not yet really exist (in the DOM). They have not been mounted yet. They have been passed to your<Form> component as a constructor prop or method as a react class. (hence the name class in React.createClass()).
As you point out, this can be circumvented by using refs, but I would not recommend it. In many cases, refs tend to be shortcuts for something that react wasn't intended for, and therefore should be avoided.
It is probably by design that react makes it hard/ impossible for parents to access a child's methods. They are not supposed to. The child's methods should be in the child if they are private to the child: they do something inside the child that should not directly be communicated upward to the parent. If that were the case, than handling should have been done inside the parent. Because the parent has at least all info and data the child has.
Now in your case, I imagine each input (child) component to have some sort of specific validation method, that checks the input value, and based on outcome, does some error message feedback. Let's say a red outline around incorrect fields.
In the react way, this could be achieved as follows:
the <Form> component has state, which includes a runValidation boolean.
as soon as runValidation is set to true, inside a setState( { runValidation: true }); react automatically re-renders all children.
if you include runValidation as a prop to all children.
then each child can check inside their render() function with something like if (this.props.runValidation) { this.validate() }
which will execute the validate() function in the child
the validate function can even use the child's state (state is not changed when new props come in), and use that for the validation message (e.g. 'please add more complicated symbols to your password`)
Now what this does not yet fix, is that you may want to do some checking at form level after all children have validated themselves: e.g. when all children are OK, submit the form.
To solve that, you could apply the refs shortcut to the final check and submit. And implement a method in your <Form> inside a componentDidUpdate() function, to check if each child is OK (e.g. has green border) AND if submit is clicked, and then submit. But as a general rule, I strongly recommend against using refs.
For final form validation, a better approach is:
add a non-state variable inside your <Form> which holds booleans for each child. NB, it has to be non-state, to prevent children from triggering a new render cycle.
pass a validateForm function as a (callback) prop to each child.
inside validate() in each child, call this.props.validateForm(someChildID) which updates the corresponding boolean in the variable in the Form.
at the end of the validateForm function in the Form, check if all booleans are true, and if so, submit the form (or change Form state or whatever).
For an even more lengthy (and way more complicated) solution to form validation in react (with flux) you could check this article.
I'm not sure if i'm missing something, but after trying what #wintvelt suggested i ran into a problem whenever i called the runValidation method inside the render method of React, since in my case runValidation changes the state by calling setState in it, thus triggering the render method which obviously is a bad practice since render method must be pure, and if i put the runValidation in willReceiveProps it won't be called the first time because the if condition is not true yet (this condition is changed in the parent component using setState, but in the first call of willReceiveProps it's still false).

Categories

Resources