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