2

I am having an issue with React ref and class component.

My simplified code is below. We see I have a component called Engine which has a property getInfo. I do a test of this.activeElement && which means it is not null so it must be a React.ReactElement<Engine>. However Typescript compiler fails with error that this does not have property getInfo as seen in screenshot below.

class Engine extends React.Component {
    getInfo(count: number): void {
        console.log('info count:', count);
    }
}

class Wizard extends React.Component {
    activeElement: null | React.ReactElement<Engine>

    topLevelGetInfo(): void {
        this.activeElement && this.activeElement.getInfo(10);
    }

    handleRef = (el: null | React.ReactElement<Engine>) => this.activeElement = el;

    render() {
        return (
            <div>
                <Engine ref={this.handleRef} />
            </div>
        )
    }
}

My tsconfig.json:

{
  "compilerOptions": {
    "module": "es6",
    "target": "es6",
    "sourceMap": true,
    "jsx": "react",
    "baseUrl": "src",
    "strict": true,
    "lib": ["dom", "es2017"],
    "moduleResolution": "node",
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  },
  "exclude": [
    "node_modules",
    "build",
    "build_test"
  ]
}

enter image description here

2 Answers 2

1

To provide inferred access to getInfo - activeElement should be asserted as type Engine (rather than React.ReactElement<Engine>) or null.

activeElement also requires an initial value of null, as undefined is not valid.

// Engine.
class Engine extends React.Component {

  // Get Info.
  getInfo(count: number) {
    console.log("Info Count:", count);
  }

  // Render.
  render = () => <div>Engine</div>;

}

// Wizard.
export class Wizard extends React.Component {

  // Active Element.
  activeElement: Engine | null = null;

  // Top Level Get Info.
  topLevelGetInfo() {
    const { activeElement } = this;
    if (activeElement) activeElement.getInfo(10);
  }

  // Handle Ref.
  handleRef = (el: Engine | null) => (this.activeElement = el);

  // Render.
  render() {
    return (
      <div>
        Wizard.
        <Engine ref={this.handleRef} />
      </div>
    );
  }

  // Did Mount.
  componentDidMount() {
    console.log("Mounting Wizard.");
    this.topLevelGetInfo();
  }

}
Sign up to request clarification or add additional context in comments.

1 Comment

Oh thank you very much @Arman!! Engine is actually one of many components it can be. The component must have the getInfo property, and it gets some props injected. Can I do React.ComponentType<{ prop1: blah }> somehow?
1

Looks like your Wizard and Engine classes aren't extending from React.Component. Try updating your class declarations like so:

class Engine extends React.Component  {
 ...

 render() { return null; }
}

class Wizard extends React.Component {
  ...
}

Also, be sure to include the required render() method for your <Engine /> component (ie as shown).

Here is a working example on codesandbox.io - hope that helps :-)

6 Comments

Oops my mistake, I forgot the extends React.Component part when typing the simplification. I still have the issue :(
@Noitidart just updated answer with link to working demo - does this help?
Where are you seeing the error? In your dev environemnt, or on codesandbox?
@DacreDenny in both. Codebox and dev. I really appreciate you trying so I +1.
@Noitidart you're welcome, this is very strange because I don't seem to get any syntax errors when I run the codebox sample - just curious to know what your tsconfig.json file looks like - can you add that to your OP ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.