Reputation: 7921
On resize even listener is not being triggered.
class MainContainer extends React.Component {
constructor(props) {
super(props);
this.containerRef = React.createRef();
this.state = {};
}
componentDidMount() {
this.containerRef.current.addEventListener("resize", this.handleResize);
}
componentWillUnmount() {
this.containerRef.current.removeEventListener("resize", this.handleResize);
}
handleResize() {
console.log("handleResize");
}
render() {
return (
<React.Fragment>
<Container ref={this.containerRef}>
<Body />
</Container>
<ShadowTop show={this.state.top} />
</React.Fragment>
);
}
}
--
export const Container = styled.div`
@media (max-width: 760px) {
position: absolute;
}
margin-top: ${({ theme }) => theme.header.height.percent}%;
margin-top: -webkit-calc(${({ theme }) => theme.header.height.pixel}px);
margin-top: -moz-calc(${({ theme }) => theme.header.height.pixel}px);
margin-top: calc(${({ theme }) => theme.header.height.pixel}px);
height: ${({ theme }) => Math.abs(100 - theme.header.height.percent)}%;
height: -webkit-calc(100% - ${({ theme }) => theme.header.height.pixel}px);
height: -moz-calc(100% - ${({ theme }) => theme.header.height.pixel}px);
height: calc(100% - ${({ theme }) => theme.header.height.pixel}px);
position: fixed;
float: none;
clear: both;
top: 0;
right: 0;
width: ${({ theme }) => 100 - theme.sidebar.width.percent}%;
width: -webkit-calc(100% - ${({ theme }) => theme.sidebar.width.pixel}px);
width: -moz-calc(100% - ${({ theme }) => theme.sidebar.width.pixel}px);
width: calc(100% - ${({ theme }) => theme.sidebar.width.pixel}px);
z-index: 2;
pointer-events: auto;
overflow: auto;
`;
What am I doing wrong here?
Am trying to detect when the div
aka Container
a styled-components
element has changed size.
Upvotes: 4
Views: 3253
Reputation: 21160
The resize
event is triggered on window
, not on individual elements. This is because the resize
event is meant to handle viewport resize, not content resize. To detect content size changes you can use ResizeObserver
.
There are a lot of ways you can incorporate this into your React project. Here is a example similar to what you have in the question:
class MainContainer extends React.Component {
constructor(props) {
super(props);
this.ulRef = React.createRef();
this.state = { todoList: [] };
// Binding methods to the current intance is only needed if you pass
// the method as an argument to another function and want acces to the
// `this` keyword in the method.
this.handleResize = this.handleResize.bind(this);
this.addTodoItem = this.addTodoItem.bind(this);
}
componentDidMount() {
this.ulObserver = new ResizeObserver(this.handleResize);
this.ulObserver.observe(this.ulRef.current);
}
componentWillUnmount() {
this.ulObserver.disconnect();
}
handleResize(entries, observer) {
console.log("handleResize", entries);
// ...
}
addTodoItem(event) {
event.preventDefault();
const formData = new FormData(event.target);
this.setState((state) => ({
todoList: [...state.todoList, formData.get("todo-item")],
}));
}
render() {
return (
<div>
<form onSubmit={this.addTodoItem}>
<input name="todo-item" />
<button>add</button>
{" "}(or press <kbd>Enter</kbd>)
</form>
<ul ref={this.ulRef}>
{this.state.todoList.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
}
ReactDOM.render(<MainContainer />, document.querySelector("#root"));
<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>
There might also be libraries out there that help you combine ResizeObserver
and React. But it doesn't hurt to understand what is happening under the hood.
For those looking for a more modern function component solution:
const { createRef, useState, useCallback, useEffect } = React;
function MainContainer() {
const ulRef = createRef();
const [todoList, setTodoList] = useState([]);
const addTodoItem = useCallback(function (event) {
event.preventDefault();
const formData = new FormData(event.target);
setTodoList(todoList => [...todoList, formData.get("todo-item")]);
}, []);
const handleResize = useCallback(function (entries, observer) {
console.log("handleResize", entries);
// ...
}, []);
useEffect(function () {
const ulObserver = new ResizeObserver(handleResize);
ulObserver.observe(ulRef.current);
return () => ulObserver.disconnect();
}, [handleResize]);
return (
<div>
<form onSubmit={addTodoItem}>
<input name="todo-item" />
<button>add</button>
{" "}(or press <kbd>Enter</kbd>)
</form>
<ul ref={ulRef}>
{todoList.map((item, index) => <li key={index}>{item}</li>)}
</ul>
</div>
);
}
ReactDOM.createRoot(document.querySelector("#root")).render(<MainContainer />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>
Upvotes: 6
Reputation: 1858
resize events are only fired on the window object
You can read more about resize event
It should be:
componentDidMount() {
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
You can add debounce
to handleResize to make it less often.
Upvotes: 1