Access a React component method / state from outsi

2020-07-30 03:14发布

问题:

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?

回答1:

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>