What is the preferred way to toggle (show/hide) a component in React? To my knowledge, there are two ways to do this.
Solution 1:
Conditionally rendering the child component inside the render() method of the parent component.
{
this.state.showUserModal ?
<UsereModal onClose={this.onModalClose} user={this.state.selectedUser}/>
: null
}
Solution 2:
Using a property at the child component which inside its own render() method returns null or the children based on the boolean.
<UsereModal show={this.state.showUserModal} onClose={this.onModalClose} user={this.state.selectedUser}/>
The second solution causes to initialize the component only once (the constructor is called once) and the first solutions do not. I am having issues with this because in need to initialize my state based on the props inside the constructor, so I am forced to use solution 2. But what are the most React way to handle this?
In both cases if the parent state or props are changed both the parent and the child components will be re-rendered. Hence there is no performance gain of the second solution. But if the child component should not be shown in the second case it will be mounted and rendered (but not shown). Taking it into account I would suggest to use the first case.
If you want to persist the DOM elements in the UI, you should go using style binding or class binding:
<UsereModal style={{display: this.state.showUserModal ? 'block' : 'none'}} />
I think the first solution is better since you do not need to initiate UsereModal component with its own state that will control should component be shown or not. I prefer also jsx notation for conditional rendering
{
this.state.showUserModal && <UsereModal onClose={this.onModalClose} user=
{this.state.selectedUser}/>
}
Related
I'm learning Vue.js. Here I want to design a checkbox component like this:
<xxx-checkbox-group>
<xxx-checkbox></xx-checkbox>
<xxx-checkbox></xx-checkbox>
</xxx-checkbox-group>
Above are two kinds, the parent checkbox-group and the child checkbox. The checkbox-group use <slot> to get the children component and have some necessary data. I want the child can use some parent's data(just use not change). It's a bit troublesome and inefficient if I put a attribute in every child's tag.
<xxx-checkbox-group>
<xxx-checkbox shape="square"></xx-checkbox>
<xxx-checkbox shape="square"></xx-checkbox>
</xxx-checkbox-group>
Can I pass the value by other ways? So that I can controll all the children's state through the parent. I think it's possible but I can't find the way to achieve that.
<xxx-checkbox-group shape="square">
<xxx-checkbox></xx-checkbox>
<xxx-checkbox></xx-checkbox>
</xxx-checkbox-group>
For me it's not fully clear what's your intention, so can you create a small jsfiddle example?
but in general, within a child component you can access to it's parent vue instance via this.$parent to read & change properties.
In Vue.js, I have a var app = new Vue({...}); and I have a component Vue.component('mycomponent', ... where I can use such component without any issue by directly adding <mycomponent></mycomponent> in html. What I wish to do is to dynamically add those component on demand maybe after a button click or when some other such event takes place. In raw JS, I'd use document.createElement... when event fires and then do el.appendChild.. to add it into html. How would I do the same with Vue.js ?
I'm not doing anything fancy with node js. This is on a single html page with <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> in the <head>.
To do this the "Vue way" usually involves the use of v-if, v-for or <component>, depending on what you want to do:
Use v-if to conditionally show a component/element based on a condition.
Use v-for to render a list of components/elements.
Use <component> to render a dynamic component/element.
So to achieve what you described in your question, you can declare a boolean data property visible like this:
data() {
return {
visible: false
}
}
and use it with v-if to control the visibility of a component in the template:
<mycomponent v-if="visible"></mycomponent>
This requires that <mycomponent> exist in the template upfront. If you don't know what kind of component you want to show, then you can either include each possibility in the template and display the one you want based on some condition:
<comp1 v-if="comp1Visible"></comp1>
<comp2 v-if="comp2Visible"></comp2>
<comp3 v-if="comp3Visible"></comp3>
or you can use <component> together with another data property (comp) which you can set to the name of the component you want to display:
<component v-if="visible" :is="comp"></component>
What you described (document.createElement followed by el.appendChild) does not exist in Vue. Vue has a strict rendering mechanism which you need to work with; it isn't possible to dynamically instantiate components and stick them into the DOM randomly. Technically you can do comp = new Vue() as an equivalent to document.createElement and then el.appendChild(comp.$el), but that probably isn't what you want to do because you would be creating an independent Vue instance that you would have to manage manually with no easy way of passing data around.
I am trying to trigger an onClick function call inside a sub-component by adding the onClick to the parent component. It does not work.
// parent component
class MainForm extends Component {
// code here
handleClick = () => {
console.log('clicked!');
}
render {
return (
<Linkbutton
text="Click Me!"
onClick={this.handleClick}>
/>
);
};
// sub component
export const LinkButton = (props) => {
return (
<button>{props.text}</button>
);
};
This did not work. The onClick function was not being called on click. I managed to fix it by adding the onClick call in the sub-component as well.
// sub component
export const LinkButton = (props) => {
return (
<button onClick={props.onClick}>{props.text}</button>
);
};
This worked. My question is why? Shouldn't the onClick be called on any element regardless of what is inside it? Why did I have to explicitly pass it down to the sub-component as well?
I would like to understand the reason for it being this way and why it wouldn't work the first way I tried. Thanks!
The reason is that the LinkButton component you created is just a javascript object, not a DOM node, so it does not have event handlers like onClick. The button element is transformed into the actual DOM element so it will have all the associated event handlers that the actual HTML element.
When you add an onClick prop to the LinkButton component, it is just a property of an object. By calling props.onClick from inside of that component you are just calling a function that is stored inside of a property, similar to this:
let props = {
onClick: function () { alert("Executed!"); }
};
props.onClick();
Hope this helps you!
In your first example, you are not actually adding the onClick event. Just passing the reference to the component doesn't mean it automatically know what to do with it.
The second example does actually add the onClick to the button, like it should, which is why it works.
In your first example, you are using "onClick" as a parameter, not as the actual event handler.
you are in little confusion. onClick props you have passed in Linkbutton component doesn't trigger your function call. in react we use props in order to communicate between components. onClick is an event in button element while onClick is props in Linkbutton component.
Unfortuantely, there is no way around that in pure React.js. If you want the parent element's function to handle click event performed on a child element, you have to explicitly pass that function as a prop to the child element.
I understand why it may seem frustrating at times, especially if you have got a tall component tree and numerous leafs of that tree that have to call a method of parent, parent of the parent, or even parent of the parent of the parent [ ... ] element.
One way to mitigate that problem (especially when your project grows big) would be to use a state store that implements the Flux pattern. Good example of that would be Redux. Instead of calling a method passed from the parent component as a prop (such method would in most cases alter the sate of the parent component), your button component would dispatch an action that would then be taken care of by a reducer function, consequently updating the global state of the application. All components of the app that rely on the part of the global state that got changed would then be updated accordingly. In this example, the button component would not communicate with the parent element directly. It would just dispatch an action.
Here is a great tutorial that you should read if you decide to learn Redux. Bare in mind that Redux is commonly considered to be somewhat difficult to start with, therefore it's best suited for big apps where initial increase in project complexity caused by the addition of Redux to the app is outweighed by added ability to avoid all the problems resulting from direct communication between components.
I ran into an issue which I do not know how to "properly" solve.
I have two components, let's call them parent and child.
child component is "generated" by a call to a function that creates it, let's call this function child creator.
parent component has a button which shows/hides the child but is also suppose to .focus() the HTML dom node which is what the child is. Now the show/hide I implemented via the state, but I do not know how to apply javascript's .focus() onto the child's HTML dom node.
There is also a small catch... the child component is being "generated" and "returned" by a call to a plain old javascript function, let's call it createChild.
This is due to the fact that child component needs to be vastly different based on what the parameters that were passed are, however, it also needs to be reused throughout the application so the createChild functions make sure that all the child components are the same, given the same inputs.
I tried passing ref to the creator, however since ref is not a prop, it is not accessible. What is the proper way to "grab" the children's dom nodes in order to .focus() them when the button is clicked since I cannot pass a ref?
Code sandbox link: https://codesandbox.io/s/lyj6x2948m
Yes, child ref is accessible since it is part of real DOM. I made a simple example with two nested components, check it out:
class Parent extends React.Component {
focusRef(ref) {
ref.focus();
}
render() {
return <Child focusRef={this.focusRef} />
}
};
class Child extends React.Component {
render() {
return (
<button
ref={childRef => this.childRef = childRef}
onMouseEnter={() => this.props.focusRef(this.childRef)}
>
When mouse enters, i get focused
</button>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById("root")
);
*:focus {
outline: 2px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root" />
Also, I strongly recommend further reading on react docs:
Okay for everyone wondering, I found a solution to the problem.
To begin with, the issue was not in the ref passing (at least not explicitly), but rather in a way controls are being created (which doesnt allow the ref to be passed).
The controlCreator function is a good old simple javascript function, and it was being used to provide a type for a React.createElement because the result of calling the controlCreator ends up being a react component.
I have however came to an understanding of why this is wrong and have instead proceeded to generate my child elements by the controlCreator and then using React.cloneElement to inject them with a ref. This way, the element is being cloned within a parent and can thus be referenced by parent's methods.
A link to a working code sandbox
Is it possible for a React component's parent to be unmounted without the child being unmounted? What work arounds exist to achieve this result? Surely there must be some (potentially hacky) way to do this.
Example
When this:
<Parent>
<Child/>
</Parent>
changes to this:
<Child/>
I would like Parent's lifecycle method componentWillUnmount to be called without Child's lifecycle method componentWillUnmount being called.
I recognize this may not be possible but was wondering if anyone had a creative solution to this problem.
Update
Here's my specific use case (hopefully I do a good enough job explaining this):
I have a higher order component which doesn't introduce any new dom elements but essentially just introduces some new context for the child component. The child component renders slightly differently depending on whether or not this context is present. Unfortunately when I remove and add the parent a new instance of the child is created and it unmounts/remounts. The only issue with this unmounting and remounting is that the child element does some dom measurements to decide whether or not to show a horizontal scroll bar and overflow menu for some of it's elements. When it unmounts/remounts there is an unsightly flash of this menu.
You could handle the rendering logic. For example inside your render function:
if (showParent) {
return (
<Parent>
<Child/>
</Parent>);
} else{
return <Child/>;
}
Then whenever the boolean showParent changes the component renders differently.
The super-parent object, calling this render() , should store the state in order to update them and preserve the child (and parent, in case you want to switch it back on).
Therefore it should also contain the state of the child, in order for it to be kept. In other words: move the model hierarchically up to the super parent.