Ad

In React How Can I Trigger A Custom Button's Click Event From A Different Compoennt's Event Handler?

- 1 answer

I have a React app which uses Material UI for it's interface. I've created a custom button component which styles the default Material UI button and also uses redux.

The render() function of my button component looks like this:

        return (
            <div className={classes.buttonWrapper}>
                <Button 
                    ref={this.props.innerRef}
                    disabled={loading || disabled}
                    onClick={this.handleClick}
                    {...other}>
                    <React.Fragment>
                        {children}
                        {this.buildLoader(loading, classes)}
                    </React.Fragment>
                </Button>
            </div>
        );

What I want is to be able to include this button on a page and have the UI trigger its click event by other means other than clicking on it. For example, on a login form I want a user who currently has focus on the password textbox to be able to trigger the button click by hitting the Return/Enter key.

I'm sure I need to use the concept of forwarding refs in React, but I'm fairly new to React and can't get it working. You can see on my button I've defined a ref set to this.props.innerRef. My button component (called WaitingButton) is exported like this:

const withInnerRef = React.forwardRef((props, ref) => <WaitingButton 
  innerRef={ref} {...props}
/>);

var component = withStyles(styles)(withInnerRef);

export default connect(mapStateToProps, mapDispatchToProps)(component);

I've then added this button to a form like this:

    <Paper>
        <TextField 
            style={{marginBottom: '8px'}}
            label="A textbox!"
            fullWidth 
            onKeyPress={(e) => { if (e.key === "Enter") this.triggerClick(); }} />
        <WaitingButton 
            ref={this.submitButton}
            variant="contained"
            color="primary"
            onClick={(e) => {
                console.log('Button clicked :)', e.target);
            }}>
            Press enter in textbox!
        </WaitingButton>
    </Paper>

See I've assigned the button's ref and in this page's constructor I've initialised the ref in the constructor using this.submitButton = React.createRef();

Finally the triggerClick looks like this:

    triggerClick() {
        console.log('CLICK', this.submitButton.current);
        this.submitButton.current.click();
    }

When I hit enter in the textbox, I can inspect the value assigned to this.submitButton.current and can see it is the Redux connect object that I've wrapped my button with. However, I also get the error this.submitButton.current.click is not a function so clearly the ref isn't getting forwarded all the way to the button itself.

I'm afraid I'm a bit lost so appealing for your help!

Ad

Answer

I just tried to reproduce your case. And I created a codesandbox for it. I think I found the problem. It seems React.forwardRef only works with prop name forwardedRef so try to rename the innerRef property to forwardedRef in your code.

const withInnerRef = React.forwardRef((props, ref) => <WaitingButton 
  forwardedRef={ref} {...props}
/>);

and also in your render() function

<Button 
    ref={this.props.forwardedRef}
    disabled={loading || disabled}
    onClick={this.handleClick}
    ...

You can try it with my simplified codesandbox https://codesandbox.io/s/intelligent-cori-rb5ce

Ad
source: stackoverflow.com
Ad