Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - Triggering a component method from another component, both belonging in the same render()

I have a custom component Foo, whose 'bar' function I want to trigger from the outside:

class Foo extends React.Component {
   bar() { ... }
}

I am rendering Foo along with a button whose purpose is to trigger Foo.bar():

render() {

 return (
     <Foo ...>
     </Foo>
     <Button onClick={I want to trigger Foo.bar()} />
 );
}

Things I have tried:

  1. Moving <Button > inside Foo, then adding onClick of <Button/> in Foo's constructor -- the binding of onClick didn't work -- I see it attached, but the <Button /> still does not trigger onClick. (or, if it can work, how to do it properly)?

Solutions that don't do what I want:

  1. I see one potential solution which suggests to listen to a click in the entire MyComponent, then do some DOM querying to find if the actual button was clicked. This won't work for me, however, because MyComponent does not own these buttons (these buttons are not MyComponent.props.children) and I'd prefer to brainstorm a solution that doesn't force me to have to move the buttons inside.

  2. Another potential solution doesn't work: I cannot have the render() return the value of <MyComponent /> into a var myComp, then inside <Button onClick={() => {myComp.blah()}} />, because <Button /> itself is instantiated inside <MyComponent /> as a child! (and, they are both in the same render() call anyways)

  3. Part b) #2 (above), using React-Component static class method, will not work either: I need this on a per-instance basis.

  4. Using app state (such as a store) is not what I want, either -- MyComponent is meant to be a re-usaable widget and cannot be tied to a specific application state or listening to any specific stores or store actions.

Any suggestions?

Thanks

like image 918
dev Avatar asked Feb 19 '17 03:02

dev


People also ask

How do you pass a function from one component to another in React?

var method = obj.method; method(); Binding methods helps ensure that the second snippet works the same way as the first one. With React, typically you only need to bind the methods you pass to other components. For example, <button onClick={this.handleClick}> passes this.handleClick so you want to bind it.

What triggers React component re renders?

As we already saw before, React re-renders a component when you call the setState function to change the state (or the provided function from the useState hook in function components). As a result, the child components only update when the parent component's state changes with one of those functions.

Can we call function from another component React?

Components are an integral part of React. Each React application consists of several components, and each component may require user interaction that triggers various actions. To achieve user interactivity, we can call functions and methods to accomplish specific operations in React.


1 Answers

2020 Update

This is how you can do it using functional components and TS

  1. Send setCustomRef to CustomComponent and down to CustomButton to set customRef
  2. Send customRef down to ClickDiv
  3. Invoke customRef.click(); inside ClickDiv

CSB https://codesandbox.io/s/triggering-component-method-from-another-component-skt8o?file=/src/App.tsx

Above Codesandbox does not always work, but I noticed it started working after some time.

import React from "react";
import "./styles.css";
import Button, { ButtonProps } from "@material-ui/core/Button";

export default function App() {
  const [customRef, setCustomRef] = React.useState<HTMLButtonElement | null>(
    null
  );
  return (
    <div className="App">
      <h3>
        React - Triggering a component method from another component, both
        belonging in the same render()
      </h3>
      <p>
        <ClickDiv customRef={customRef} />
      </p>
      <CustomComponent setCustomRef={setCustomRef} />
    </div>
  );
}

type Props = {
  setCustomRef: React.Dispatch<React.SetStateAction<HTMLButtonElement | null>>;
};

const CustomComponent = ({ setCustomRef }: Props) => {
  const anchorRef = React.useRef<HTMLButtonElement>(null);
  const { current } = anchorRef;
  React.useEffect(() => {
    if (current) {
      setCustomRef(current);
    }
  }, [current, setCustomRef]);

  return <CustomButton ref={anchorRef}>Custom Button</CustomButton>;
};

const CustomButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (props, ref) => {
    const onClick = () => {
      alert("CustomButton clicked");
    };
    return (
      <Button ref={ref} {...props} variant="contained" onClick={onClick} />
    );
  }
);

type ClickDivProps = {
  customRef: HTMLButtonElement | null;
};
const ClickDiv = ({ customRef }: ClickDivProps) => {
  const onClick = () => {
    if (customRef) customRef.click();
  };
  return <button onClick={onClick}>Triger Custom Button Click</button>;
};
like image 87
keemor Avatar answered Sep 28 '22 06:09

keemor