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
Related
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.
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}/>
}
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.
Is there a way to access the React tree down a component from within it?
For instance when I use the React-devtool extension I am able to navigate along the React tree. Is it possible to do such navigation from within the code of a component to know everything below it?
I know about children, but it does not work when parents components simply render children components, ie in Parent component I have something like
render() {
<Child1 />
}
then in Child1 component I have
render() {
<Child2 />
}
etc.
and I'd like to know from Parent that there is below a Child1 and then a Child2.
I have had a look at this._reactInternalInstance._instance._renderedComponent._currentElement where I can see that, for instance, Parent renders a Child1. But from this I cannot go further to see what Child1 renders.
I managed to solve a similar problem (needed to access state of a foreign Component's child) with:
return <Child ref={this.childRef} />
this.childRef.current._reactInternalFiber.child.child.memoizedState;
Hope it helps & YMMV ;)
I have a set of components app->page->list_container->list->item
My goal is to notify the app that click happened on item level.
If there is simple relation like parent->child I could use props and do something like: <Child onClick={this._onClick}> ... and then use this.props.onClick() to make a callback.
But what is the best native React-style receipt for doing the same trick with a tree of components? How to notify the app, that item was clicked without calling to Flux/Reflux, Elm and other supported libs?
Standard react way:
Passing onClick function as a prop down your component tree is the standard react-way of doing this.
In <app>:
<page onClick={this._onClick}>
In <page>:
<list_container onClick={this.props.onClick}>
Etcetera.
Or you could use:
<list_container {...this.props}>
To automatically pass down any prop from parent component to the child component.
In a deep tree, this can and will get quite tedious/ lot of work. React was not designed for this purpose.
React is made for (top-down) smart and fast component-tree rendering.
The frameworks for the flux pattern you mention are designed to handle the other interactive functions.
Alternative shortcut (not recommended):
A shortcut you could possibly use is to add a listener directly on the DOM, inside your <app> component, that handles the click event on the item:
In <app> component, add:
componentDidMount: function() {
var itemElementInDOM = document.getElementById('myItem');
itemElementInDOM.addEventListener('click',this._onClick);
}
And in your <item> component, give the item a (unique) id.
I would generally NOT recommend this:
In a typical react tree setup, the lower level components (like
<item>) may be rendered more than once, and then you would need
additional logic to ensure that each ID is unique.
You would also
need to add some additional smarts to make sure you remove the listener if the
item(s) in question are removed from the DOM.