14

I need to launch a function that I have in the child component of a parent button but I have not got it, someone knows some form, I have already seen some examples but I can not find something that can help me since they only handle property changes

this is my code parent:

import React from 'react';
import ReactDom from 'react-dom';
import AppBar from 'material-ui/AppBar';
import injectTapEventPlugin from 'react-tap-event-plugin';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Menux from '../componentes/menux';

class App extends React.Component {
     constructor(props) {
    super(props);
  }
  render() {
    return (
<div>
         <AppBar
    title="Aqui empezara todo"
    onLeftIconButtonTouchTap = {Menuxr.handleToggle}
  />
     <Menux />
</div>
        );
    }

}

injectTapEventPlugin();
ReactDom.render( <MuiThemeProvider><App/></MuiThemeProvider>,document.getElementById('workspace'));

child

import React from 'react';
import Drawer from 'material-ui/Drawer';
import AppBar from 'material-ui/AppBar';
import MenuItem from 'material-ui/MenuItem';
import RaisedButton from 'material-ui/RaisedButton';

export default class Menux extends React.Component {

  constructor(props) {
    super(props);
    this.state = {openmenu: false};
  }

  handleClose = () => this.setState({openmenu: false});
  handleToggle = () => this.setState({openmenu: !this.state.openmenu});
  render() {
    return (
      <div>
        <RaisedButton
          label="Toggle Drawer"
          ref="menu"
          onTouchTap={this.handleToggle}/>
        <Drawer width={200} openSecondary={false} open={this.state.openmenu} >
          <AppBar title="menu"
                   onLeftIconButtonTouchTap = {this.handleClose}
          />
            <MenuItem onTouchTap={this.handleClose}>Mi perfil</MenuItem>
            <MenuItem onTouchTap={this.handleClose}>Mis usuarios</MenuItem>
            <MenuItem onTouchTap={this.handleClose}>Mis unidades</MenuItem>
        </Drawer>
      </div>
    );
  }
Jesus Juarez
  • 217
  • 1
  • 2
  • 8

4 Answers4

18

That's not how React functions, unfortunately. You are trying to use Menux.handleToggle as the callback of the onLeftIconButtonTouchTap prop, right? So when someone clicks the left icon button, you want the Menux component to execute the handleToggle function?

That won't work for two reasons:

  1. When you call Menux.handleToggle, you are not calling it on an instance! Menux is a reference to the class itself. It doesn't have the internal state that you are expecting. Moreover, handleToggle is an instance method. This means it can only be executed on instances of the Menux class.
  2. You may be referencing the Menux class, but as discussed above, that is not the same instance that React creates when you render the <Menux /> element.

It's really hard to adequately articulate why it's wrong, it's just fundamentally wrong on a few levels hehe. Long story short: you just cannot call the function of a class like that and expect it to change an instance of that class that is not being specified.


Let's move past that. Now, there's a convoluted way of getting your code to work in the way you've written it, but I think it's better to help you understand a more idiomatic way of doing it. That is to say, a more "React way" of getting done what you want.

We want to "Lift the State Up". This concept is this article: https://facebook.github.io/react/docs/lifting-state-up.html

I highly recommend reading that article.

Why do we "lift state up?" Well in React, child nodes don't communicate with each other. Only children and parents communicate with each other. In fact, parents communicate by providing data and functions, children communicate only by calling the functions parents give them.

Imagine you're a kid, one of 5 siblings. You each have a cell phone, but you don't know the phone numbers of your siblings. You only have your parents phone number. But you need to convey some information to some of your siblings. What do you do? You call your parents phone number, relay them the information, and the parents will call your siblings and relay the data to them.

That's how React works.

So how do we apply this to your situation? Instead of the Menu determining itself when it's open, let's have the parent tell the Menu when it's open. This way, the parent can give both the Menux component and the AppBar component a function that they can use to say "Hey mom, can you close the menu?"

We will lift the state from the Menux component to the App component.

class App extends React.Component {
     constructor(props) {
    super(props);
    this.state = {
       openmenu: false
    }
  }
  handleClose = () => this.setState({openmenu: false});
  handleToggle = () => this.setState({openmenu: !this.state.openmenu});
  render() {
    return (
<div>
         <AppBar
    title="Aqui empezara todo"
    onLeftIconButtonTouchTap = {this.handleToggle}
  />
     <Menux openmenu={this.state.openmenu} handleToggle={this.handleToggle} handleClose={this.handleClose} />
</div>
        );
    }

}

Ah ha! Now our App determines when the menu is closed and when it's not. It is the single source of truth! But, it provides AppBar and Menux with some functions that they can call if they want to change the state in the parent. So how will Menux change?

export default class Menux extends React.Component {

  render() {
    return (
      <div>
        <RaisedButton
          label="Toggle Drawer"
          ref="menu"
          onTouchTap={this.props.handleToggle}/>
        <Drawer width={200} openSecondary={false} open={this.props.openmenu} >
          <AppBar title="menu"
                   onLeftIconButtonTouchTap = {this.props.handleClose}
          />
            <MenuItem onTouchTap={this.props.handleClose}>Mi perfil</MenuItem>
            <MenuItem onTouchTap={this.props.handleClose}>Mis usuarios</MenuItem>
            <MenuItem onTouchTap={this.props.handleClose}>Mis unidades</MenuItem>
        </Drawer>
      </div>
    );
  }

It uses the functions that the parent passed it in order to set the state in the parent. A nice, easy, reusable component!

This is the React way of doing things. Parent-child only communication, state and functions passed down to children, children call those functions to change state higher up in the tree. Beautiful!


"But, but, but...I really don't want to do things the React way!"

Fiiiiine. Thankfully, someone else answered (so I don't have to :]) with a very simple way to accomplish what you want to do, without having to restructure your application to ahem a more idiomatic way.

aaronofleonard
  • 2,546
  • 17
  • 23
5

Use a ref to keep a reference to the child component, so you can call handleToggle from the parent as you want:

<Menux ref={(menu)=>{this.menu = menu}}/>

Then:

<AppBar
  title="Aqui empezara todo"
  onLeftIconButtonTouchTap = {this.menu.handleToggle}
/>
mguijarr
  • 7,641
  • 6
  • 45
  • 72
2

Found here: https://medium.com/@nugen/react-hooks-calling-child-component-function-from-parent-component-4ea249d00740

import React, { forwardRef, useRef, useImperativeHandle } from 'react';
export default function ParentFunction() {
    const childRef = useRef();
    return (
        <div className="container">
            <div>
                Parent Component
            </div>
            <button
                onClick={() => { childRef.current.showAlert() }}
            >
            Call Function
            </button>
            <Child ref={childRef}/>
        </div>
    )
}
const Child = forwardRef((props, ref) => {
    useImperativeHandle(
        ref,
        () => ({
            showAlert() {
                alert("Child Function Called")
            }
        }),
    )
    return (
       <div>Child Component</div>
    )
})
Jorg
  • 111
  • 1
  • 3
0

For calling a parent event from a child, I used this solution https://stackoverflow.com/a/71464748/1770571 and it works properly with me

Salma Gomaa
  • 952
  • 1
  • 13
  • 18