When check validation of child component, Which is better take a reference of child component and call its validate method or make an output with Boolean flag and handle it in parent component?
When you add reference to a child, you have following benefits:
You have access to all methods in child component.
You have access to other variables in the same.
You can reset the form after submitting.
In case of form control, you can play with validators.
I hope, you will get the answer from above all points.
Related
I have two Components.
In the second component, "date-detail-filter" I always keep track for boolean value, and want to access this data in my parent component.
do you know how to use $emit?
In your date-detail-filter component
you can add this to your method. this.$emit('your-event-name', 'your payload')
and in your main component.
<date-detail-filter #your-event-name="functionName()"/>
functionName(payload) {
your logic here to hide the caret
}
$emit is used to pass data from child component to parent component via event.
I have a Component titled 'CollectionsPage' where I am trying to create a reference to a child, and call one of its methods via its onClick function.
The reason for this is because I want the child to handle its own state, rather than pass it all down from the parent component. The child is called 'CollectionsCreateForm' and I currently just have a function in it called 'test'. I tried to call the function but it never runs -- however, when I tried the same function from a different component, it worked. Is there something about AntD's form-within-a-modal that is causing me difficulty?
I have tried changing the reference, and I also passed in 'wrappedComponentRef' to child to ensure that wasn't the issue. I need to be able to access form in the child, but AntD says it needs to be passed in as props from the parent. I would love to put the 'onCreate' method in the child, so the modal handles everything that it should without help from the parent, but I believe it needs a reference to the formRef.
I have made an example of my issue on a sandbox. Here is the link.
When using antd to create a form you basically use the Higher-Order Component - (Form.create(Component)) that wraps your component with the test method inside.
this.testRef.current will refer to your Higher-Order Component basicly Form.create class not your own component.
You can use wrappedComponentRef like so to get reference to your own component:
<CollectionCreateForm
wrappedComponentRef={(inst) => this.formRef = inst}
/>
and then simply call this.formRef.test();
Here is a working example:
https://codesandbox.io/s/n34ppwnq9j
I understand that there's two ways to pass components data: props and state. But why would one need a prop over a state? It seems like the state object could just be used inside the component, so why pass the prop parameters in markup?
Props are set externally by a parent component. E.g.;
render() {
return <ChildComponent someProp={someValue}/>;
}
State is set internally, and often triggered by an user event within a child. E.g.;
handleUserClickedButton: () {
this.setState({
buttonClicked: true
});
},
render() {
return <button onClick={this.handleUserClickedButton}/>;
}
So, props are a way for data to go from parent to child. State is a way for data to be managed within a singular component, and possibly have changes to that data triggered by children. In effect, they represent data traveling in 2 opposite directions, and the way in which they are passed is entirely unique.
There are two ways to "pass" or access data from outside your component but state is not one of them.
The two ways are:
Props - which a parent component pass down to the child component.
Context - which you can "skip" the direct parent in the tree.
The state is an internal object which no other component has access to it unless you pass it explicitly (via the two ways mentioned above).
So basically your question is not accurate as you can't really compare the two.
I think what you are really asking is why using a state-less instead of a state-full component.
Which you can find an answer here in Stack-overflow or in other websites.
Edit
A followup to some of your comments.
why does the child not just have a shared state? for example, each
component (or sub-component) could just do a "this.state" to get the
current state of the program
The same way you can't share or access private objects in other
functions.
This is by design, you share things explicitly and you will pass
only what the component needs. For example, look it this page of
stack-overflow, lets say the voting buttons are components, why
would i pass them the whole state if it only needs the vote count
and 2 onClick event listeners? Should i pass the current logged in
user or maybe the entire answers rendered in this page?
so you can't pass state between a parent to child? for example, can't
the parent change the state and then the child gets the new state
This is exactly what the props or context should do, provide an API for sharing data between parents and children though we keep it in a one way data flow, from parents to children, you can't pass props upwards. but you invoke handlers passed down to your child components and pass data through that handler.
I have really simple example in react. Parent component pass data to the child component throug props. Inside child component i can change this data, also new data can be passed to the child component from the parent at any time. Also on the parent i have save button. After pressing it i should show validation message if some validations did not pass.
I have implemented it in such a way. From parent i pass callback to notify parent that validity of the data has changed. Inside child i perform validation in three places:
componentDidMount - to validate initial data
componentWillReceiveProps - to validate data that can be passed from the parent
onChange to validate entered data
Any time child perform validation of data i call callback to inform parent about validity. The problem is in componentWillReceiveProps. With setState in parent this part causes infinite loop - see picture below.
Please check jsfiddle Here in console you can see that i restricted infinite loop to 10 iterations to prevent you browser from crash.
As you can see there is infinite loop - because React call componentWillReceiveProps in not too smart way - every render cycle instead of callind it only when props actually changed.
I am really would like to know what is the react way to solve this issue. Should i store child state only in child ? I also tried to store child validity in parent out of state - but my coworkers said that this is not react way - why ?
Here's how I see your challenge:
you want to have your validation method in your child component, because it is specific to this type of child (e.g. a password input field). - but in react, no outside component (except grandchildren) are allowed to directly call this method.
you want your parent to know about the validaty of each of the children components (e.g. to determine of all the fields validate and a form submit is allowed) - so you need the result of the validation method in the parent component, and this must be state
Your co-workers are correct: react does not like you storing component-wide variables outside of state. Because such variables are completely independent from react lifecycle methods etc. and would get you in debugging hell quickly.
I would advise you to make following changes to prevent the endless loop:
do NOT store the validity of the child in the child state. Save the result + any message in the parent, and pass it as a props to the child. Child only renders the props.
implement shouldComponentUpdate(), which checks if any of the props or state variables have changed. If not return false, otherwise, return true.
move your call to validate() from componentWillReceiveProps to componentWillUpdate(). This ìs called after shouldComponentUpdate(). So only if props or child state have changed, will validation (and re-render) take place.
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).