Redux-forms Call alternate submit from ComponentWillRecieveProps - javascript

redux-forms version: 6.6.3
react version: 15.5.0
I want to call different submit functions from componentWillRecieveProps function in my react component.
componentWillReceiveProps(nextProps) {
if (nextProps.updateTierConfigState == "ValidationFulfilled"
&& nextProps.updateMyConfigValidationClean) {
console.log('CWRP calling submit()')
//this.props.submit(); //THIS CALLS DEFAULT on FORM's onSubmit
this.props.handleSubmit(this.updateSubmit().bind(this))
}
else {
this.props.handleSubmit(this.createSubmit().bind(this))
}
}
updateSubmit(values) {
//do stuff
}
createSubmit(values) {
//do stuff
}
I have see examples like this: https://github.com/erikras/redux-form/issues/711#issuecomment-191850962
But, I have not been able to call handleSubmit successfully. It does not call the passed in function.
I have debugged into handleSubmit and it returns very quickly with out calling the specified submit function.

You are immediately invoking the functions when you pass them in. When you reference the submit functions, you don't need to include the () after the function name. this.updateSubmit().bind(this) should be this.updateSubmit. You also don't need the bind here.
Change: this.props.handleSubmit(this.updateSubmit().bind(this))
To: this.props.handleSubmit(this.updateSubmit)

I discovered that I needed to do two things.
Invoke the custom submit method using this line: this.props.handleSubmit((values)=> this.submitUpdate(values))() Note the added "()" at the end.
Move this line (and a surrounding if condition) to the componentDidUpdate() method. I'm using props in the submit method to decide the state of validation of the component and the props from mapStateToProps are not assigned to this.props before componentWillReceiveProps or componentWillUpdate.
Here is my new automatic submit call:
componentDidUpdate() {
//moved this here so that this.props is updated from nextState
if (this.props.updateMyConfigState == "ValidationFulfilled"
&& this.props.updateMyConfigValidationClean) {
this.props.handleSubmit((values) => this.submitUpdate(values))();
}
}
I am aware that this is outside of the redux-form recommendation for validation and I recognize that this this might not be the best way to handle validation. My current reasoning is:
It allows me to have multiple submission validation actions (separates ones for create and update user actions) where each has their own error and warnings behavior. In particular, Error: disallow action and WarningsOnly: allow but require extra user approval.
Visually present the errors/warnings in a separate list outside of the gui elements.

Related

React, how can I submit updates to Input box when clicking outside the component

I am looking for a solution in which a user can enter text into an input, and when they click anywhere outside of the component, it submits, saves the value.
In my case I have a top level component which dynamically generates Text Input components.It passes in the properties including an UpdateValue function.
In these functional components I am using the ability to add an event listener to the document, and then I detect if the click comes from outside the component in question. Having done a lot of research on stackoverflow, this seems to be the agreed upon method.
useEffect(() => {
document.addEventListener('mousedown', handleClick);
return () => {
document.removeEventListener('mousedown',handleClick);
}
},[])
const handleClick = (e) => {
if(node.current.contains(e.target))
{
return;
}
updateValue()
}
I have this working, in that the function gets called when I click outside the component, however, the function invoked on the parent component no longer seems to have access to the state..they appear all empty. I can only guess that because we are using a generic javascript function initially, the call doesn't have access to react stack.
So, my question is, how can I make this work where I want a nested functional component that has an input, and when a user clicks off component, it runs the updateValue function which can then use some internal state data to properly update this in the DB.
FYI, I have a onchange() on the input, which updates the value in the parent component already.
So, UpdateValue() is a trigger to basically submit final changes to DB.
Your updateValue should be a callback that you create inside your functional component. That should take dependencies on the state that you define there. Use React.useCallback

Handling form state with NGRX / #Effects

I have a form and on submit I dispatch an action which is caught by an effect. The effect then does the http call. I'm wondering how off the back of this action completing / failing I would do the following:
show a success message once the action completes
reset all the fields ready for when the form is next used
show an error off the back of the action failing
I understand I could re-dispatch an action to populate the store with several flags success, error etc. However, resetting the form will probably be done by calling a function. Would it be acceptable to subscribe to the store and calling the relevant reset function in the child? It's almost as if I'd like components to be able to listen to actions just like effects can.
If your effect-rest does not affect the store, but only should display a notification - then there are two ways I'd say:
1) Inject Actions in your component as you suggested yourself:
class SomeComponent {
constructor(actions: Actions) {
actions.ofType(SOME_EVENT)
.do(myThing)
.takeUntil(componentIsDestroyed)
.subscribe();
}
}
2) Or don't go the effects- and actions-way at all and just call a simple service-method:
class SomeComponent {
constructor(myService: CoolService) {
}
onClick(): void {
mySevice.makeRequest()
.do(myThing)
.subscribe();
}
}

How to get the return state from a sendAction method in a component

I am not using Ember Data (I do not know if it solves the issue). I have a component and click the button will invoke an external event handler defined inside the controller. I would like to know the return state from a sendAction method. See image below for details.
enter text in textarea
click "BTN SAVE"
handle action in index controller
perform ajax request in btnPressed actions obj
MY QUESTION How to notify the component the return state from the step (4)
Once I know the request state I can perform the correspond action(s) in my component.
Thanks
You can't have return value from action if you're using old action handling mechanism(using sendAction()), what you need are closure actions. Using closure actions you can call action defined in controller directly in component and have return value from it.
Here's your example using closure actions.
index.hbs
{{note-editor btnPressed=(action 'btnPressed')}}
In note-editor.hbs
<button onclick={{action 'actionBtnPressed'}}>Save</button>
And then in note-editor.js you can call btnPressed when button is clicked.
actionBtnPressed() {
let val = this.btnPressed();
// val is value returned from controller's btnPressed action
}
You can read more about closure actions here.
https://github.com/emberjs/rfcs/blob/master/text/0050-improved-actions.md
After reading the post Correct way to make an ajax call from EmberJs component?,
I move the AJAX code inside the controller to the component to solve this issue.
actionBtnPressed() {
const card = {
front: this.get('cardFront'),
back: this.get('cardBack'),
tags: this.get('tags')
};
if (this._validateCardFront()) {
this.get('ajax').createCard(card)
.done((data) => {
this._cleanInputs();
})
.fail((err) => {
});
}
}
ajax is a service that is injected into the component.

react-redux store not updating within onClick function

I'm experiencing this weird issue where my react-redux store is updating, but is not updating within the function that calls the actions.
this.props.active is undefined, then I set it to an integer with this.props.actions.activeSet(activeProc), but it remains undefined and enters the next if condition.
I know my app is working because everything else works with this.props.active having the correct value.
Is this supposed to happen?
edit:
After doing some testing, it appears that the state remains the same inside the onClick function.
All calls to console.log(this.props) made within the onClick function show no change to the state, but adding setTimeout(() => {console.log(this.props)}, 1) at the end to test shows that the state is being updated.
Other parts of the app are working as intended, with state changes applied immediately.
But I still don't understand what is going on.
Component function code
() => {
console.log(this.props.active); // undefined
if (this.props.active === undefined && this.props.readyQueue.length > 0) {
let activeProc = this.props.readyQueue[0];
this.props.actions.readyPop();
this.props.actions.activeSet(activeProc); // set to an integer
this.props.actions.execStateSet("Running");
}
console.log(this.props.active); // still remains undefined
if (this.props.active === undefined) {
this.props.actions.execStateSet("Idle");
}
}
function mapStateToProps(state, props) {
return {
active: state.ProcessReducer.active,
};
}
Action code
export const activeSet = (procId) => {
return {
type: 'ACTIVE_SET',
procId
}
}
Reducer code
case 'ACTIVE_SET':
return Object.assign({}, state, {
active: action.procId
});
Your Redux state updates synchronously with the dispatch of your action. Your reducer has executed by the time the dispatch call returns.
However, React isn't Redux. Redux tells React-Redux's wrapper component that the state has changed. This also happens before dispatch returns.
React-Redux then tells React that the component needs to be rerendered by calling forceUpdate. React then waits until it feels it's a good time to take care of that. I haven't looked, but it probably uses setImmediate or equivalent but it's async. This allows React to batch updates and maybe there are other reasons.
In any case, the React-Redux wrapper component will get rendered by React when the time comes and it'll use your mapStateToProps to distill theprops out of the state and then passes them to React as props for your actual component. Then, when React feels it's an okay time, it calls your render method or function. It may do all kinds of things in before that, such as calling componentWillReceiveProps or rendering some other component that also needs rendering. In any case it's none of our business. React does its thing. But when your Render function is called, your props will now reflect the new state.
You shouldn't rely on new state in an onClick handler. The onClick should only call the bound action creator, which I guess is now more aptly called an action dispatcher. If something needs to be done with the new state, you should use Redux-Thunk middleware and create a thunked action creator. These have access to getState and if they don't perform any internal async stuff, then the entire action can actually be just as synchronous as a simple dispatch (not that you'd need that in a simple onClick handler).
Finally, React is very asynchronous in nature. Think of it as telling React what you want (component + props) and letting React take it from there. If React needs to know how to turn a component into DOM elements, it'll call your component's render function. How or when React does is thing is an implementation detail that doesn't concern us.

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