I'm working on React. There's another team which exposes us a component which has a button in it. It is a dependency for our repository. I want that button to be focused when a particular action is done. I want to do it using refs or any other acceptable way.
myCode.js
focusExternalButton() {
// some code that focuses the button in the external component
}
render() {
return {
<div>
<ExternalButtonComponent/>
<button onClick={this.focusExternalButton}>submit</button>
</div>
}
}
ExternalButtonComponent.js
render() {
return <div><button id="btn-external">This is a button</button></div>
}
How do I put focus on the external button when I click my button? I'm thinking of refs but I'm not sure how I can achieve it.
It surprises me a little that the external component uses id
s as rendering it more than once in a page will lead to duplicate id
s on the page which isn't valid HTML.
Nonetheless, yes, you can query the DOM directly, though I would probably do this once on mount and store the result returned element in a ref
.
const { useEffect, useRef } = React;
function App() {
const buttonRef = useRef();
useEffect(() => {
buttonRef.current = document.getElementById('btn-external');
}, []);
function focusExternalButton() {
buttonRef.current.focus();
};
return (
<div>
<ExternalButtonComponent label="This is a button" />
<button type='button' onClick={focusExternalButton}>submit</button>
</div>
);
}
function ExternalButtonComponent({label}) {
return <div><button id="btn-external">{label}</button></div>;
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
#btn-external:focus { box-shadow: 0 0 0 3px rgba(21, 156, 228, 0.4);}
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
However, to avoid having to do this every time you use the component you can wrap the external component and forward the ref.
const WrappedExternalButtonComponent = forwardRef((props, ref) => {
useEffect(() => {
ref.current = document.getElementById('btn-external');
}, []);
return <ExternalButtonComponent {...props} />
});
const { useEffect, useRef, forwardRef } = React;
const WrappedExternalButtonComponent = forwardRef((props, ref) => {
useEffect(() => {
ref.current = document.getElementById('btn-external');
}, []);
return <ExternalButtonComponent {...props} />
});
function ExternalButtonComponent({label}) {
return <div><button id="btn-external">{label}</button></div>;
}
function App() {
const buttonRef = useRef();
function focusExternalButton() {
buttonRef.current.focus();
};
return (
<div>
<WrappedExternalButtonComponent ref={buttonRef} label="This is a button" />
<button type='button' onClick={focusExternalButton}>submit</button>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
#btn-external:focus { box-shadow: 0 0 0 3px rgba(21, 156, 228, 0.4);}
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
Or even generalize it further by creating a utility that takes a Component
and a query function as arguments and returns a wrapped component.
const wrapExternalComponent = (Component, query) => forwardRef(({ children, ...props }, ref) => {
useEffect(() => {
ref.current = query();
}, []);
return (
<Component {...props}>
{children}
</Component>
);
});
const WrappedExternalButtonComponent =
wrapExternalComponent(ExternalButtonComponent, () => document.getElementById('btn-external'));
const { useEffect, useRef, forwardRef } = React;
const wrapExternalComponent = (Component, query) => forwardRef(({ children, ...props }, ref) => {
useEffect(() => {
ref.current = query();
}, []);
return (
<Component {...props}>
{children}
</Component>
);
});
function ExternalButtonComponent({label}) {
return <div><button id="btn-external">{label}</button></div>;
}
const WrappedExternalButtonComponent =
wrapExternalComponent(ExternalButtonComponent, () => document.getElementById('btn-external'));
function App() {
const buttonRef = useRef();
function focusExternalButton() {
buttonRef.current.focus();
};
return (
<div>
<WrappedExternalButtonComponent ref={buttonRef} label="This is a button" />
<button type='button' onClick={focusExternalButton}>submit</button>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
#btn-external:focus { box-shadow: 0 0 0 3px rgba(21, 156, 228, 0.4);}
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>