Centralized function to delegate form onSubmit to Flux Actions - javascript

I have a react app with many different forms I'm looking to not have to write the same code over and over for each form, and I'm looking for a simple way to centralize the onSubmit() handlers for all forms. From a central location I can trigger the specific Flux action that will trigger an ajax call.
I'm picturing one function that takes care of all form requests.
function (event) {
event.preventDefault()
var $elm = $(event.target)
var d = {}
d.method = $elm.attr('method')
d.action = $elm.attr('action')
d.data = $elm.serialize()
console.log(d)
}
From here I can check the action and method and make a switch for each possibility.
Does this make sense within the Flux architecture?

You will anyway need to collect data from specific components, so you will have to write individual functions for it, but the rest of task like sending an action can be done in the onSubmit function of a parent component. So delegate all onSubmit events in children components with data to the onSubmit functions of parent component via props.
You can also add an identifier like we do as constants in dispatchers, to identify particular onSubmit event and then in parent component use switch statements to send particular actions based on identifier.
If there's a use case, I guess it makes sense.

Related

React hooks onclick event with multiple params without unnecessary rerender?

I am using react hooks and functional components and was wondering how I can add multiple params to an react onClick event.
I know there are different options to achieve this. In the past I used this style below (from https://reactjs.org/docs/handling-events.html):
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
But now I am facing this exact described problem from the official react docs. I am getting to many rerenders because of these arrow functions in the onClick attribute:
The problem with this syntax is that a different callback is created
each time the LoggingButton renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering. We generally
recommend binding in the constructor or using the class fields syntax,
to avoid this sort of performance problem.
I have put my function already in a useCallback hook. But if I use this function in a onClick event with an arrow function it will trigger rerenders again.
If I change it to the function reference only it is not triggering rerenders.
So far this is fine.
But: How do I add multiple parameters to this functionreference when using react hooks and functional components.
Will I get by default always the e (event parameter?) as first parameter?
Can somebody explain to me when and how I am getting the react event parameter and when I will not receive this event?
How can I add multiple params beside the event parameter in my onClick attribute?
For example:
What if I have this function and want to use it in the react onClick attribute, prevent unnecessary rerender and add multiple different parameter in the function call
const myClickFunction = (e, value1, value2, value3) => {
// ...
}
// this would trigger rerenders because of the arrow function how do I prevent this?
<button onClick={(e) => myClickFunction(e, "input1", "input2", "input3")}>
Click me
</button>
One trick I like to use in this case is to "bind" the parameters to rendered element using data attributes
const myClickFunction = (e) => {
const value1 = e.currentTarget.getAttribute('data-value1')
const value2 = e.currentTarget.getAttribute('data-value2')
const value2 = e.currentTarget.getAttribute('data-value2')
}
// this would trigger rerenders because of the arrow function how do I prevent this?
<button onClick={myClickFunction} data-value1="a" data-value2="b" data-value3="c">
Click me
</button>
This way you can memoise your function using useCallback safely and you can reuse the same function if you want to pass it to array of children for example. This is not ideal, you couple parents and children and you can only use data which is serializeable to string (basically only primitives).
Better solution would be to store your values somewhere out of component tree so you can access them without closures (for example in redux-thunk you don't need to pass a lot of stuff around, you can just get data you need from store directly by calling getState)

Ember adding action to template for parent/child communication

I am trying to define a custom action to my Ember component & want to communicate between parent/child.
In my-child.js, I have
actions: {
someCustomAction: function() {
let self = this;
self.sendAction('someCustomAction');
},
}
And I catch the same in my-parent.js as below;
actions: {
someCustomAction: function (){
console.log("Inside someCustomAction....");
}
}
Now, with the above code, control/action does not come to my-parent.js
I have to add "someCustomAction" to the template as below
In my-parent.hbs
{{my-child someCustomAction="someCustomAction"}}
I wanted to know the exact reason for the same. Why does just doing sendAction not automtically work ?
Here's how I would go about it while following the data down, actions up (DDAU) pattern.
First, you pass the action you want to fire in your child using the action helper.
{{my-child someActionName=(action 'parentAction')}}
Then, you retreive the action and fire it from the child component.
this.get('someActionName')(params);
Ember Twiddle Complete Example
This is because components are not contextually aware. So the actions need to be tied together manually. But this also gives you the ability to send different actions on the same component and even additional args as well.
Components are isolated from their surroundings, so any data that the
component needs has to be passed in.
https://guides.emberjs.com/v2.15.0/components/passing-properties-to-a-component/
If the component is truly isolated, actions are as well.
https://guides.emberjs.com/v2.15.0/components/triggering-changes-with-actions/#toc_passing-the-action-to-the-component

Flux store dependency with async actions

I'm having problems understanding what is the best way to do this using the Flux pattern. Say for example that I have a userStore and I listen to it. Once it changed, I need get the user.name and access colors[user.name] - but the colors object comes from another colorsStore store I have. Here's the essence of it:
var self = {};
userStore.addListener(function(user) {
// dependency with the colors store
var color = self.colors[user.name]
})
colorsStore.addListener(function(colors) {
self.colors = colors;
})
actions.getUser() // modifies userStore
actions.getColors() // modifies colorsStore
The problem is that the two actions are async (they get the data from an AJAX call for instance). With this in mind, the userStore might change before the self.colors variable is populated from the other store.
How is this handled using the Flux pattern? Does the Dispatcher help with this somewhat? Sorry but I'm new to the Flux pattern. Intuitively I would simply call the async actions in the appropriate order such as:
actions.getColors() // need to populate self.colors before running getUser()
.then(actions.getUser())
But was wondering if there was a more Flux-way of doing this.
Your setup is fine from flux perspective.
Your component needs to be able to handle different possible (stores) states generated by your actions, which could possibly include:
user store has old/no data, colors store already has newest data
user store has newest user data, colors store still has old data
If you want any of these states to be visible to the user in some way (eg show loading indicator, show old color/ default color while waiting for newest color), then the react-flux way is to deal with these states inside your component.
If you do not want to show anything about these states to user, you have two options:
inside your component, fire the actions.getUser() from inside the colorStore listener function (quick and dirty solution)
change the setup to prevent the unwanted store state to trigger component update
For the second solution, you could typically do:
have you component fire both actions
both listeners trigger the same function getStateFromStores()
this function fetches state from both stores, and only does component update (setState()) if user and colors match
That way, your async calls can come back in any order.
If I understand your problem correctly, you can use waitFor for this case. Also there's the discussion about "waitFor vs combining stores into one", so a combined store can solve your problem as well.

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).

How can I access child components values from a parent component when they are added dynamically?

Current Working Example
I am creating a search form that has a varying number of input elements based on the users selection from a select box.
I have broken this up into three components, a wrapper called SearchContainer, a select box called SearchSelect, and the inputs within components called SearchWithTwo and SearchWithOne just for the sake of the example.
App
└─SearchContainer Form
│ SearchSelect
│ ... any one of the multiple search inputs (SearchWithOne, SearchWithTwo)
When a user changes the value of the select box the related component which contains the inputs is loaded. The component could have anywhere from one to ten inputs. All the examples I've seen mention using ref which would be great if my inputs weren't changing.
I currently have it working by using the following in the onSubmit handler for SearchContainer
handleSubmit: function(e) {
e.preventDefault();
var form = this.getDOMNode();
[].forEach.call(form.elements, function(e){
// get the values
});
// submit the values to get results.
}
However this doesn't feel like the proper way to be doing this. Is there a better recommended way to iterate through the children components and read their state? Or can I somehow pass the children into the parent state and get the values that way?
I think I have a solution in the form of a fork of your fiddle, and I'll cover the main ideas below.
First, I'm no React expert, but I like the idea of it, and I know it's gaining popularity so I want to learn more. What I don't know is the right way to use composition or inheritance to reduce the code duplication shown below.
Basically, my idea is to add a method to each search class that exposes its state to calling classes. This is implemented here as a very simple function inside the createClass call:
getData: function() {
return this.state;
},
It's so simple, there has to be a way to create a base class or mixin class with this method and then inherit/compose over it with other classes. I'm just not sure how. For now, you can just copy-paste these lines wherever it makes sense to expose a component's state.
Keep in mind, this is anathema to the Flux architecture everyone loves. In Flux, state always comes from a source object which lives outside the React components.
Anyway, abandoning larger architecture concerns for now, you can just grab that state variable by calling getData in the handleSubmit method. No DOM traversal required:
handleSubmit: function(e) {
e.preventDefault();
var form = this.getDOMNode(),
fd = new FormData(form);
var submitData = this.state.SearchBox.getData();
// submit the values to get results.
},

Categories

Resources