Search code examples
reactjstypescriptref

How do I make a class component method accessible outside of the component in React and Typescript?


I need to be able to call a class component's methods outside of the class as I am building a component library that compiles to umd and then is available via the window object.

At the moment the methods in my classes are private but I want to make them 'callable' from outside. I have seen that it can be done using React refs, but I can only find specific examples where ref is used to make a classes methods available to other components.

import React, { Component, ReactElement } from 'react';


interface DashboardProps {
  props?: Function;
}

interface DashboardState {
  ColorToggled: boolean;
  myRef?: Function;
  current?: Function;
}

class Dashboard extends Component<DashboardProps, DashboardState> {
  private myRef: React.RefObject<HTMLInputElement>;

  constructor(props: DashboardProps) {
    super(props);
    this.myRef = React.createRef();
    this.state = {
      colorToggled: false,
    };
  }


  public changeColor = (): void => { // I want to make this method accessible outside of the component
    this.setState((prevState) => ({
      colorToggled: !prevState.colorToggled,
    }));
  };

  render(): ReactElement {
    // console.log(this.current.myRef();
    return (
      <>
        <div
          role="textbox"
          tabIndex={0}
          style={{
            padding: '20px',
            backgroundColor: this.state.colorToggled === true ? '#f00' : '#00f',
          }}
        >
          <br />
        </div>
        <input type="text" ref={this.myRef} />
      </>
    );
  }
}

export default Dashboard;  

I have only just starting putting the above together so it is not functional at all but not quite sure how to progress.


Solution

  • In a component library, almost always one or multiple components like Dashboard are exported as a module (in your case with UMD also on the global scope) and the package consumer provides the React runtime. That also means, you can rely on the React environment in the library - just pass in props to your root component from the client.

    If you really wanted to call a public method on a class component imperatively from another component, you could do that with refs, as you said.

    Playground

    Dashboard with public changeColor method:

    class Dashboard extends React.Component<{}, State> {
      ...
      changeColor = (): void => {
        this.setState(prevState => ({
          colorToggled: !prevState.colorToggled
        }));
      };
    }
    

    (for function components there is useImperativeHandle)

    Add a ref to Dashboard and invoke changeColor:

    const App = () => {
      let compRef = React.createRef<ColorComp>();
      return (
        <div>
          <button onClick={() => compRef.current!.changeColor()}>
            Toggle color
          </button>
          <ColorComp ref={compRef} />
        </div>
      );
    };
    

    There are possibly much more alternatives, to name only a few:

    These have to be justified somehow, as they add complexity and coupling to the architecture. So I would stick to the first section (via props and/or refs), unless you really have special requirements.

    Hope, that helps.