Home > Mobile >  How can I use forwardRef in React Component?
How can I use forwardRef in React Component?

Time:04-12

Can someone help me? I'm creating a component inside React, and I want to make it more accessible using forwardRef. In my case, I'm making a button and I'm using the button's properties, and a few more I've done to make it more dynamic.

This is a summary of my code.

export interface ButtonProps 
extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
  children?: React.ReactNode;
  loading?: boolean;
}

class Button extends React.Component<ButtonProps> {

  render() {
    const {
      ...otherProps
    } = this.props;

    return (
      <button {...(otherProps)}></button>
    )
  }
}

export default Button;

I tried to start something, but right away it gave an error

const ForwardedElement = React.forwardRef<ButtonProps, HTMLButtonElement> (
  (props: ButtonProps, ref) => <Button {...props}/>
)

export default ForwardedElement;

CodePudding user response:

I suggest you to use useImperativeHandle hook

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. Let's visualize that with an example.

Here's a component as search bar

import React, {forwardRef, useImperativeHandle, useRef} from "react";
import {Form} from "reactstrap";

const SearchBar = (props, ref) => {
    const buttonRef = useRef<any>();

    useImperativeHandle(ref, () => ({
        getCurrentValue: () => {
            return buttonRef.current ? buttonRef.current["value"] : '';
        },
        setCurrentValue: (value) => {
            if (buttonRef.current) {
                buttonRef.current["value"] = value;
            }
        }
    }));

  return (
      <Form className="p-3 w-100" onSubmit={(e) => props.onSubmitHandler(e)}>
          <div className="form-group m-0">
              <div className="input-group">
                  <input
                      type="text"
                      className="form-control"
                      placeholder="Search ..."
                      aria-label="Word to be searched"
                      ref={buttonRef}
                  />
                  <div className="input-group-append">
                      <button className="btn btn-primary" type="submit">
                          <i className="mdi mdi-magnify" />
                      </button>
                  </div>
              </div>
          </div>
      </Form>
  );
}

export default forwardRef(SearchBar);

This is the header component in which we call our search bar component

import React, {useEffect, useRef, useState} from 'react';
import SearchBar from '../Form/Search/SearchBar';
import Router from 'next/router';

const Header = () => {
    const mobileSearchRef = useRef<any>();
    const [search, setSearch] = useState<any>(false);

    const codeSearchHandler = (e) => {
        e.preventDefault();

        setSearch(!search);

        if (mobileSearchRef.current) {
            if (mobileSearchRef.current.getCurrentValue() == '') {
                return;
            }
        }

        Router.push({
            pathname: '/search',
            query: {
              searchTerm: mobileSearchRef.current
                  ? mobileSearchRef.current.getCurrentValue()
                  : ''
            },
        });

        mobileSearchRef.current.setCurrentValue('');
    };

    return (
        <React.Fragment>
            <header id="page-topbar">
                <div className="navbar-header">
                    <div className="d-flex">
                        <div className="dropdown d-inline-block d-lg-none ms-2">
                            <button
                                onClick={() => {
                                    setSearch(!search);
                                }}
                                type="button"
                                className="btn header-item noti-icon mt-2"
                                id="page-header-search-dropdown"
                            >
                                <i className="mdi mdi-magnify" />
                            </button>
                            <div
                                className={
                                    search
                                    ? 'dropdown-menu dropdown-menu-lg dropdown-menu-end p-0 show'
                                    : 'dropdown-menu dropdown-menu-lg dropdown-menu-end p-0'
                                }
                                aria-labelledby="page-header-search-dropdown"
                            >
                                <SearchBar
                                    id="headerSearchBar"
                                    ref={mobileSearchRef}
                                    onSubmitHandler={(e) => codeSearchHandler(e)}
                                />
                            </div>
                        </div>
                    </div>
                </div>
            </header>
        </React.Fragment>
    );
};

export default Header;

If we look at the header component, we can see that we get the input value of search bar component using mobileSearchRef and getCurrentValue method. We can also set its value using setCurrentValue method.

CodePudding user response:

You have to pass the ref aside the spread props:

export interface ButtonProps 
extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
  children?: React.ReactNode;
  loading?: boolean;
  ref?: React.RefObject<HTMLButtonElement>
}

class Button extends React.Component<ButtonProps> {

  render() {
    const {
      ...otherProps,
      ref
    } = this.props;

    return (
      <button {...otherProps} ref={ref}></button>
    )
  }
}

export default Button;

const ForwardedElement = React.forwardRef<ButtonProps, HTMLButtonElement> (
  (props: ButtonProps, ref) => <Button {...props} ref={ref}/>
)

export default ForwardedElement;

now it should work, see this question

  • Related