[terra-responsive-element] Does not honor proper React.useEffect behavior
See original GitHub issueBug Report
Description
terra-responsive-element
doesn’t work well when used with React.useEffect
, specifically when creating an effect that focuses on an element when mounting, e.g. focus on a button:
function Example() {
const buttonRef = React.useRef(null);
React.useEffect(() => {
const { current: button } = buttonRef;
button.focus();
}, []); // only on mount (empty dep list means on mount)
return (
<button type="button" ref={buttonRef}>A button with a ref</button>
);
}
This pattern is common when using modals (i.e. terra-modal-manager
) to shift focus into the modal (from the body) when opened (mounted) for a11y and proper focus management.
Some consumers are using terra-responsive-element
in the following way:
// pretend that this was mounted using `disclosureManager.disclose({ ... })`
function ModalContent() {
const titleRef = React.useRef(null);
React.useEffect(() => {
const { current: title } = titleRef;
if (title) {
// this will never fire, using this example setup
title.focus();
} else {
console.error("No title ref to focus");
}
}, []);
const tinyContent = (
<div>
<h1 tabIndex="0" ref={titleRef}>Tiny Title Here</h1>
<p>Tiny content here</p>
<button type="button">Some other focusable thing</button>
</div>
);
const largeContent = (
<div>
<h1 tabIndex="0" ref={titleRef}>Large Title Here</h1>
<p>Large content here</p>
<button type="button">Some other focusable thing</button>
</div>
);
return (
<ResponsiveElement
responsiveTo="window"
tiny={tinyContent}
large={largeContent}
/>
);
}
I have reservations about this pattern (should probably hoist the ResponsiveElement
above or use the React.useContext(ActiveBreakpointContext)
from terra-breakpoints
), but it doesn’t work as expected.
Steps to Reproduce
Check out the code/deployments below:
Here’s a minimal (working?, not as expected tho) example using terra-responsive-element
:
Another example that works as expected, using terra-breakpoints
inside terra-modal-manager
(as described in the example above):
Additional Context / Screenshots
Expected Behavior
React.useEffect()
(especially, React.useEffect(() => { ... }, [])
expected behavior should be honored.
Possible Solution
I would probably suggest either deprecating ResponsiveElement
in preference for using terra-breakpoints
ActiveBreakpointContext
(like in the example repo I linked above), since it can be heavy when used in actual apps (at least as I have seen consumers use it) or providing guidance on better patterns of use (e.g. hoisting above, like state, and using it like you would with ActiveBreakpointContext
).
Environment
- Component Name and Version: terra-responsive-element 5.10.0
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (8 by maintainers)
Top GitHub Comments
We may want to address the point you made in the summary of this issue first and determine if we want to deprecate the terra-responsive-element completely.
Components using the terra-responsive-element with
responsiveTo
set towindow
should be able to uplift onto the terra-breakpoints package and remove the dependency on the responsive element all together. But components and projects using the responsive element to be responsive to their parent containers may have a hard time finding an alternative solution to the responsive element.I think there is value in keeping the controlled responsive element around. The onChange and onResize callbacks open a lot of functionality for responsive components. It acts as an abstraction and wrapper for a consistent resize observer. The uncontrolled responsive element also has some valid use-cases, but most situations should be using the controlled component or could be converted into a controlled component to achieve the same functionality.
I don’t believe the issues mentioned above can be resolved with the uncontrolled version of the component. If we consider this issue a real bug we may want to deprecate it. At a minimum if we choose to keep the uncontrolled responsive element we’d need to document the caveats to using it.
This is likely a side effect of the same issue here: https://github.com/cerner/terra-core/issues/2673
The uncontrolled responsive element doesn’t know which element to render until it’s mounted and calculated the available size.
At first glance I don’t think this is something that can be resolved with the uncontrolled responsive element. However I’d be interested in seeing if the issue is the same with the controlled responsive element.
Would you be willing to recreate these examples with the controlled responsive element and see if the same issues are encountered?
https://engineering.cerner.com/terra-ui/components/terra-responsive-element/responsive-element/responsive-element
Example from the doc site: