I have two components:
Parent.js
export default class Parent extends React.Component {
handleTagHierClick(e) {
var excel = this.refs.workbook;
excel.click();
}
render() {
return (
<button onClick={(e) => this.handleTagHierClick(e)} className="btn btn-default btn-block">Download Tag Hier</button>
<Child/>
)
}
}
And Child.js
import React from 'react';
export default class Child extends React.Component {
render() {
return (
<div ref='workbook'></div>
)
}
}
How can I trigger a click on the div element from the Child component when I click on the button element from the Parent component? I tried with ref but that won't work since the elements are on different components...
If I understood you right, you do it like
class Parent extends React.Component {
handleTagHierClick(e) {
this.child.someMethod();
}
render() {
return (<div>
<Child ref={(obj) => { this.child = obj; }}/>
<button
onClick={(e) => this.handleTagHierClick(e)}/>
</div>
);
}
}
Inside child
import React from 'react';
export default class Child extends React.Component {
someMethod(){
// Here you can trigger click on the div
this.div.click();
}
render() {
return (
<div ref={(obj) => { this.div = obj; }}></div>
)
}
}
Did you try to add a ref to the button?
<button ref='myButton'...
Then replace the variable excel by:
var excel = this.refs.myButton.refs.workbook;
Related
I have two buttons named selectall in two different components . in Acompoent I am having selectall button and its selectall function. I am trying to call this selectall function from Bcompoent. Here Bcompoent and Acompoent has relation with Ccompoent. can someone tell me how to trigger selectAll from Bcompoent.
Acompoent
selectAll={selectAll}
const selectAll = (e) => { console.log ("selectAll")}
For this you will need to pass a method from your parent component to both A & B components like so:
class Parent extends React.Component{
.
.
.
selectAll = (data) => {
console.log({data});
}
render(){
return(
<ComponentA selectAll={this.selectAll} />
<ComponentB selectAll={this.selectAll} />
)
}
}
class ComponentA extends React.Component{
.
.
.
render(){
return(
<button onClick={() => this.props.selectAll(data)>click me</button>
)
}
}
class ComponentB extends React.Component{
.
.
.
render(){
return(
<button onClick={() => this.props.selectAll(data)>click me</button>
)
}
}
class Parent extends React.Component {
selectAll = (e) => { console.log ("selectAll")}
render () {
return(
<Child eyeColor={selectAll} />
)
}
}
class Child extends React.Component {
render () {
return(
<div style={{backgroundColor: this.props.eyeColor} />
)
}
}
Or You can try this <Child eyeColor={selectAll()} />
If you have reusable functions create another component with those functions (Util component) and export all the functions you need to reuse.
Utils.js file
import React from 'react'
function reUseFun1(/*parameters*/) {
//f1 body
}
function reUseFun2(/*parameters*/) {
//f2 body
}
export {
reUseFun1,
reUseFun2
}
When the a util function needs to use
import React from 'react';
import Utils from './Utils';
function Component() {
return(
<button onClick={Utils.reUseFun1(/*parameters*/)}> Cick Here </button>
)
}
export default Component;
I have a FileUploader which needs to trigger from outside the component via prop, i have been battling for hours and could not figure out.
export class App {
let button = (<button>Click me please!</button>);
render() {
return (
<div className="app">
{button}
<FileUploader trigger={button} />
</div>
);
}
}
export class FileUploader {
constructor(props) {
this.trigger = props.trigger; // <button>Click me please!</button>
// do something to attach an onclick trigger element to simulate a
// click to the file input
}
render() {
return (
<div className="file-uploader">
<input type="file" style="display: none;">
<div>
// display the file image and some other additional file info
</div>
</div>
);
}
}
i wish that this.trigger will simulate a click to the input, which will open up a window to select files .. how can i do this? much appreciated
You can pass a empty ref to FileUploader from the parent component, and we will assign that ref to the input-file, and when you click on the button you have the access to the input, so you can manually trigger click
export default class App extends React.Component {
constructor(props) {
super(props);
this.fileInput = React.createRef(); // Create a empty ref
}
openFileInput = () => {
this.fileInput.current.click();
// since we have access to the fileinput, we can trigger manual click
};
render() {
return (
<div>
<button type="button" onClick={this.openFileInput}>
open file browser in child component
</button>
.......
<FileInput fileInputRef={this.fileInput} />
// pass the empty ref to the file input so we can assign it to the input-file
</div>
);
}
}
export class FileInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="file-uploader">
....
<input
type="file"
ref={this.props.fileInputRef} // assign the ref to the input
style={{ display: "none" }}
/>
</div>
);
}
}
DEMO
Here is a small example of how you can pass function to child component from parent and call them from child component.
import React from "react";
import ReactDOM from "react-dom";
class Children extends React.Component {
constructor(props) {
super(props);
this.onPress = props.onPress;
}
render = () => {
return (
<div>
<button onClick={this.onPress} color="success">
Child Click
</button>
</div>
);
};
}
class Parent extends React.Component {
fromParent = () => {
alert("Called from children");
};
render() {
return <Children onPress={this.fromParent} />;
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
Is it possible to create a method in Parent class in React and then pass it on to the Child and use it there.
So basically I would create a button in my Parent class, pass the function on to the Child and when the Button is clicked, the child will know about it and Parent will not really care for it?
class App extends Component {
clickMade = () => {
//This should be left empty
};
render() {
return (
<div className="App">
<Button onClick={this.clickMade}>Click me </Button>
<Child clickMade={this.clickMade} />
</div>
);
}
}
export default App;
And the Child:
class Child extends Component {
constructor(props) {
super(props);
this.handleClick = this.props.clickMade.bind(this);
}
handleClick = () => {
console.log("Click in child");
}
render() {
return null;
}
}
export default Child;
And a sandbox for this: CodeSandbox
App.js
class App extends Component {
clickMade = () => {
this.childRef.handleClick();
};
render() {
return (
<div className="App">
<Button onClick={this.clickMade}>Click me </Button>
<Child
ref={ref => {
this.childRef = ref;
}}
/>
</div>
);
}
}
Can I use children in React Container or is it wrong?
For example, I have a list of buttons(ActionButton) that are grouped together (ActionMenu).
import React from 'react';
class App extends React.Component {
render() {
return (
<ActionMenu>
<ActionButton name="New" icon="add" />
<ActionButton name="Delete" icon="remove" />
</ActionMenu>
)
}
}
class ActionMenu extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert('Click!');
}
render() {
return React.Children.map(this.props.children, (button) =>
React.cloneElement(button, {
onClick: this.handleClick
})
);
}
}
function ActionButton({ name, icon, onClick }) {
return <button class={icon} onClick={onClick}>{name}</button>
}
You can use children regardless of whether it's a component of container.
"[children are] especially common for components like Sidebar or Dialog that represent generic 'boxes'."
In your case you have a menu, which falls into this category.
https://reactjs.org/docs/composition-vs-inheritance.html
I think this is what you are after. Actually you should just put the children in its closest parent instead of its grandpa.
import React from 'react';
import { render } from 'react-dom';
function ActionButton({ name, handleClick }) {
return <button onClick={handleClick}>{name}</button>
}
class ActionMenu extends React.Component {
constructor(props) {
super(props);
}
handleClick = () => {
alert('Click!');
}
render() {
return (
<div>
<ActionButton name="add" handleClick={this.handleClick}/>
<ActionButton name="remove" handleClick={this.handleClick} />
</div>
);
}
}
class App extends React.Component {
render() {
return (
<ActionMenu />
)
}
}
render(<App />, document.getElementById('root'));
You can try to run it in sandbox.
By the way, using bind is quite redundant now, we can use public class fields syntax, which is already ECMA stage 2.
I created a custom Accordion component which again consist of two child components called AccordionTitle and AccordionContent:
The AccordionTitle component has a button. When clicked, the AccordionContent part toggles its style from display:none to block and back when clicked again.
AccordionTitle.js
class AccordionTitle extends Component {
constructor() {
super();
this.show = false;
}
toggle() {
this.show = !this.show;
if (this.props.onToggled) this.props.onToggled(this.show);
}
render() {
return (
<div style={this.props.style}>
<Button onClick={e => this.toggle(e)} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
AccordionContent.js
class AccordionContent extends Component {
render() {
let style = this.props.style ? this.props.style : {};
style = JSON.parse(JSON.stringify(style));
style.display = this.props.show ? 'block' : 'none';
return (
<div style={style}>
{this.props.children}
</div>
);
}
}
export default AccordionContent;
Also, I use the following parent component:
Accordion.js
class Accordion extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Accordion.Title = AccordionTitle;
Accordion.Content = AccordionContent;
export default Accordion;
Now, when I use the Accordion component, it's possible that I might need multiple accordions in a row which would look like this:
ProductAccordion.js
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: false,
};
}
toggled() {
this.setState({
show: !this.state.show,
});
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, this)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
As you can see, I am grabbing the toggled Event from Accordion.Title and I bind it to the prop show of Accordion.Content via the toggled() method.
Now, this works perfectly fine as long as there is just one product, but if there are more of them, clicking on the button will toggle all AccordionContent instances.
How can I change this so that only the content-part which belongs to the title that contains the clicked button will be toggled?
I also have the feeling that the component Accordion should take care of this (rather than ProductAccordion) by allowing Accordion.Title to delegate the toggled event directly to its sibling Accordion.Content. How can I achieve this?
I would suggest storing the index of the open item in state, instead of a boolean. Then in your render, show={this.state.show} would be something like show={this.state.show === i}.
Full example:
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: null,
};
}
toggled(event, ind) {
const index = this.state.index;
this.setState({ show:ind === index ? null : ind });
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, i)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show === i}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
and this
class AccordionTitle extends Component {
constructor() {
super();
}
render() {
return (
<div style={this.props.style}>
<Button onClick={this.props.onToggled} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;