Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access a React component method / state from outside the component

I have embedded a reactjs component into an existing HTML page by referencing it's ID like described in React's tutorial:

ReactDOM.render(
  <Page />,
  document.getElementById('my-react-compnent')
);

Then:

<div id="my-react-compnent></div>

The component is displayed and works as expected.

Now I want to link a button located on that page to my component (to be specific I would like to retrive its state, but for the example even invoking one of its methods would be fine).

In other words - when clicking the outside button, I want to invoke a method from the Page component?

How can I do that?

like image 646
Andy Thomas Avatar asked Jan 02 '23 19:01

Andy Thomas


1 Answers

The Old Recommendation

Assigning the returned value from ReactDOM.render does allow access to the component and it's methods. For example, in a simple app, we might have:

const PageComponent = ReactDOM.render(<Page />, document.getElementById("app"));

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

However, according to the docs this might be changed or deprecated and is not recommended.

The New Recommendation

The new recommendation is to attach a callback ref to the root element. Using the same example above:

const PageComponent = ReactDOM.render(<Page ref={(pageComponent) => {window.pageComponent = pageComponent}}/>, document.getElementById("app"));

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

This also works for child components.

Here's a full example:

class ChildComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0
    }
  }

  returnCounter = () => {
    return this.state.counter;
  }
  
  increment = (event) => {
    event.stopPropagation();
    this.setState(prevState => {
      return {
        counter: prevState.counter + 1
      }
    })
  }
  
  render() {
    return (
      <div onClick={this.increment}>
        Child Value - {this.state.counter} - Click to increment
      </div>
    )
  }
}

class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0
    }
  }

  returnCounter = () => {
    return this.state.counter;
  }
  
  increment = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter + 1
      }
    })
  }

  render() {
    return (
      <div onClick={this.increment}>
        <div>Parent Value - {this.state.counter} - Click to increment</div>
        <ChildComponent ref={(childComponent) => {window.childComponent = childComponent}}/>
      </div>
    )
  }
}

ReactDOM.render(<Page ref={(pageComponent) => {window.pageComponent = pageComponent}} />, document.getElementById("app"));

const parentBtn = document.getElementById("parentButton");
parentBtn.addEventListener("click", event => {
  alert(window.pageComponent.returnCounter());
});

const childBtn = document.getElementById("childButton");
childBtn.addEventListener("click", event => {
  alert(window.childComponent.returnCounter());
});
<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="app"></div>

<button id="parentButton">Get Parent State</button>
<button id="childButton">Get Child State</button>
like image 189
Brett DeWoody Avatar answered Jan 05 '23 15:01

Brett DeWoody