Call a React component method from outside

Reactjs

Reactjs Problem Overview


I want to call a method exposed by a React component from the instance of a React Element.

For example, in this jsfiddle. I want to call the alertMessage method from the HelloElement reference.

Is there a way to achieve this without having to write additional wrappers?

Edit (copied code from JSFiddle)

<div id="container"></div>
<button onclick="onButtonClick()">Click me!</button>

var onButtonClick = function () {

    //call alertMessage method from the reference of a React Element! Something like HelloElement.alertMessage()
    console.log("clicked!");
}

var Hello = React.createClass({displayName: 'Hello',
    
    alertMessage: function() {
        alert(this.props.name);                             
    },
                               
    render: function() {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});

var HelloElement = React.createElement(Hello, {name: "World"});
 
React.render(
    HelloElement,
    document.getElementById('container')
);

Reactjs Solutions


Solution 1 - Reactjs

There are two ways to access an inner function. One, instance-level, like you want, another, static level.

Instance

You need to call the function on the return from React.render. See below.

Static

Take a look at ReactJS Statics. Note, however, that a static function cannot access instance-level data, so this would be undefined.

var onButtonClick = function () {
    //call alertMessage method from the reference of a React Element! 
    HelloRendered.alertMessage();
    //call static alertMessage method from the reference of a React Class! 
    Hello.alertMessage();
    console.log("clicked!");
}

var Hello = React.createClass({
    displayName: 'Hello',
    statics: {
        alertMessage: function () {
            alert('static message');
        }
    },
    alertMessage: function () {
        alert(this.props.name);
    },

    render: function () {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});

var HelloElement = React.createElement(Hello, {
    name: "World"
});

var HelloRendered = React.render(HelloElement, document.getElementById('container'));

Then do HelloRendered.alertMessage().

Solution 2 - Reactjs

You can do like

import React from 'react';

class Header extends React.Component{

	constructor(){
		super();
		window.helloComponent = this;
	}

    alertMessage(){
       console.log("Called from outside");
    }

	render(){
    
	  return (
      <AppBar style={{background:'#000'}}>
        Hello
      </AppBar>
	  )
	}
}

export default Header;

Now from outside of this component you can called like this below

window.helloComponent.alertMessage();

Solution 3 - Reactjs

I've done something like this:

class Cow extends React.Component {

    constructor (props) {
        super(props);
        this.state = {text: 'hello'};
    }

    componentDidMount () {
        if (this.props.onMounted) {
            this.props.onMounted({
                say: text => this.say(text)
            });
        }
    }

    render () {
        return (
            <pre>
                 ___________________
                < {this.state.text} >
                 -------------------
                        \   ^__^
                         \  (oo)\_______
                            (__)\       )\/\
                                ||----w |
                                ||     ||
            </pre>
        );
    }

    say (text) {
        this.setState({text: text});
    }

}

And then somewhere else:

class Pasture extends React.Component {

    render () {
        return (
            <div>
                <Cow onMounted={callbacks => this.cowMounted(callbacks)} />
                <button onClick={() => this.changeCow()} />
            </div>
        );
    }

    cowMounted (callbacks) {
        this.cowCallbacks = callbacks;
    }

    changeCow () {
        this.cowCallbacks.say('moo');
    }

}

I haven't tested this exact code, but this is along the lines of what I did in a project of mine and it works nicely :). Of course this is a bad example, you should just use props for this, but in my case the sub-component did an API call which I wanted to keep inside that component. In such a case this is a nice solution.

Solution 4 - Reactjs

1. With React hooks - useImperativeHandle + useRef

const MyComponent = ({myRef}) => {
  const handleClick = () => alert('hello world')
  useImperativeHandle(myRef, () => ({
    handleClick
  }), [/* dependencies (if any) */])
  return (<button onClick={handleClick}>Original Button</button>)
}

MyComponent.defaultProps = {
  myRef: {current: {}}
}

const MyParentComponent = () => {
  const myRef = React.useRef({})
  return (
    <>
      <MyComponent 
        myRef={myRef}
      />
      <button onClick={myRef.current.handleClick}>
        Additional Button
      </button>
    </>
  )
}

2. With only React hook - useRef

const MyComponent = ({myRef}) => {
  const handleClick = () => alert('hello world')
  myRef.current.handleClick = handleClick
  return (<button onClick={handleClick}>Original Button</button>)
}

MyComponent.defaultProps = {
  myRef: {current: {}}
}

const MyParentComponent = () => {
  const myRef = React.useRef({})
  return (
    <>
      <MyComponent 
        myRef={myRef}
      />
      <button onClick={myRef.current.handleClick}>
        Additional Button
      </button>
    </>
  )
}

Good Luck...

Solution 5 - Reactjs

With the render method potentially deprecating the returned value, the recommended approach is now to attach a callback ref to the root element. Like this:

ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));

which we can then access using window.helloComponent, and any of its methods can be accessed with window.helloComponent.METHOD.

Here's a full example:

var onButtonClick = function() {
  window.helloComponent.alertMessage();
}

class Hello extends React.Component {
  alertMessage() {
    alert(this.props.name);
  }

  render() {
    return React.createElement("div", null, "Hello ", this.props.name);
  }
};

ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));

<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="container"></div>
<button onclick="onButtonClick()">Click me!</button>

Solution 6 - Reactjs

You can just add an onClick handler to the div with the function (onClick is React's own implementation of onClick) and you can access the property within { } curly braces, and your alert message will appear.

In case you wish to define static methods that can be called on the component class - you should use statics. Although:

"Methods defined within this block are static, meaning that you can run them before any component instances are created, and the methods do not have access to the props or state of your components. If you want to check the value of props in a static method, have the caller pass in the props as an argument to the static method." (source)

Some example code:

	const Hello = React.createClass({

		/*
			The statics object allows you to define static methods that can be called on the component class. For example:
		*/
		statics: {
			customMethod: function(foo) {
			  return foo === 'bar';
			}
		},


		alertMessage: function() {
		    alert(this.props.name);                             
		},

		render: function () {
		    return (
		        <div onClick={this.alertMessage}>
		        Hello {this.props.name}
		        </div>
		    );
		}
	});

	React.render(<Hello name={'aworld'} />, document.body);

Hope this helps you a bit, because i don't know if I understood your question correctly, so correct me if i interpreted it wrong:)

Solution 7 - Reactjs

It appears statics are deprecated, and the other methods of exposing some functions with render seem convoluted. Meanwhile, this Stack Overflow answer about debugging React, while seeming hack-y, did the job for me.

Solution 8 - Reactjs

If you are in ES6 just use the "static" keyword on your method from your example would be the following: static alertMessage: function() { ... },

Hope can help anyone out there :)

Solution 9 - Reactjs

class AppProvider extends Component {
  constructor() {
    super();

    window.alertMessage = this.alertMessage.bind(this);
  }

  alertMessage() {
    console.log('Hello World');
 }
}

You can call this method from the window by using window.alertMessage().

Solution 10 - Reactjs

method 1 using ChildRef:

public childRef: any = React.createRef<Hello>();

public onButtonClick= () => {
    console.log(this.childRef.current); // this will have your child reference
}

<Hello ref = { this.childRef }/>
<button onclick="onButtonClick()">Click me!</button>

Method 2: using window register

public onButtonClick= () => {
    console.log(window.yourRef); // this will have your child reference
}

<Hello ref = { (ref) => {window.yourRef = ref} }/>`
<button onclick="onButtonClick()">Click me!</button>

Solution 11 - Reactjs

I use this helper method to render components and return an component instance. Methods can be called on that instance.

static async renderComponentAt(componentClass, props, parentElementId){
         let componentId = props.id;
        if(!componentId){
            throw Error('Component has no id property. Please include id:"...xyz..." to component properties.');
        }

        let parentElement = document.getElementById(parentElementId);

        return await new Promise((resolve, reject) => {
            props.ref = (component)=>{
                resolve(component);
            };
            let element = React.createElement(componentClass, props, null);
            ReactDOM.render(element, parentElement);
        });
    }

Solution 12 - Reactjs

With React17 you can use useImperativeHandle hook.

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef:

function FancyInput(props, ref) {
    const inputRef = useRef();
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus();
        }
    }));

    return <input ref={inputRef} ... />;
}

FancyInput = forwardRef(FancyInput);

In this example, a parent component that renders would be able to call inputRef.current.focus().

Solution 13 - Reactjs

For dynamic components I used the getDerivedStateFromProps method with props.

You can create function that update the props of the child component, The getDerivedStateFromProps in the child component will handle the update of the props for you. For example:

class Parent extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {  selectMachine: '1' };

        this.setComponent = null;
       
    }

     handleMachineChange = (e) =>{
        this.setState({selectMachine: e.target.value})
    }

}

class Child extends React.Component
{
    state = {
        programForm: {
            machine_id: '1',
           }
    }

    constructor(props)
    {
        super(props);
    }


    static getDerivedStateFromProps(props, state) {
        if(props.selectMachine !== state.programForm.machine_id){
            //Change in props
            return{
                programForm:  { ...state.programForm, machine_id: props.selectMachine }
            };
        }
        return null; // No change to state
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1341217View Question on Stackoverflow
Solution 1 - ReactjsJeff FairleyView Answer on Stackoverflow
Solution 2 - ReactjsKushal JainView Answer on Stackoverflow
Solution 3 - ReactjsgitaarikView Answer on Stackoverflow
Solution 4 - ReactjsAakashView Answer on Stackoverflow
Solution 5 - ReactjsBrett DeWoodyView Answer on Stackoverflow
Solution 6 - ReactjsDanillo FelixdaalView Answer on Stackoverflow
Solution 7 - ReactjsphilipkdView Answer on Stackoverflow
Solution 8 - ReactjsdarmisView Answer on Stackoverflow
Solution 9 - ReactjsMygel BergstresserView Answer on Stackoverflow
Solution 10 - ReactjsMohideen bin MohammedView Answer on Stackoverflow
Solution 11 - ReactjsStefanView Answer on Stackoverflow
Solution 12 - ReactjsBilal SaidumarovView Answer on Stackoverflow
Solution 13 - ReactjsOrasView Answer on Stackoverflow