当浏览器窗口调整大小时,如何让React重新渲染视图?
背景
我有一些块,我想在页面上单独布局,但我也希望他们更新时,浏览器窗口的变化。最终的结果将是像Ben Holland的Pinterest布局,但使用React而不仅仅是jQuery。我还有一段路要走。
Code
这是我的应用程序:
var MyApp = React.createClass({
//does the http get from the server
loadBlocksFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
mimeType: 'textPlain',
success: function(data) {
this.setState({data: data.events});
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentWillMount: function() {
this.loadBlocksFromServer();
},
render: function() {
return (
<div>
<Blocks data={this.state.data}/>
</div>
);
}
});
React.renderComponent(
<MyApp url="url_here"/>,
document.getElementById('view')
)
然后我有Block组件(相当于上面Pinterest例子中的Pin):
var Block = React.createClass({
render: function() {
return (
<div class="dp-block" style={{left: this.props.top, top: this.props.left}}>
<h2>{this.props.title}</h2>
<p>{this.props.children}</p>
</div>
);
}
});
和block的列表/集合:
var Blocks = React.createClass({
render: function() {
//I've temporarily got code that assigns a random position
//See inside the function below...
var blockNodes = this.props.data.map(function (block) {
//temporary random position
var topOffset = Math.random() * $(window).width() + 'px';
var leftOffset = Math.random() * $(window).height() + 'px';
return <Block order={block.id} title={block.summary} left={leftOffset} top={topOffset}>{block.description}</Block>;
});
return (
<div>{blockNodes}</div>
);
}
});
问题
我应该添加jQuery的窗口大小调整?如果有,在哪里?
$( window ).resize(function() {
// re-render the component
});
有没有更“React”的方式来做到这一点?
我知道这个问题已经有人回答了,但我只是想把我的解决方案作为最重要的答案分享给大家,虽然很好,但现在可能有点过时了。
constructor (props) {
super(props)
this.state = { width: '0', height: '0' }
this.initUpdateWindowDimensions = this.updateWindowDimensions.bind(this)
this.updateWindowDimensions = debounce(this.updateWindowDimensions.bind(this), 200)
}
componentDidMount () {
this.initUpdateWindowDimensions()
window.addEventListener('resize', this.updateWindowDimensions)
}
componentWillUnmount () {
window.removeEventListener('resize', this.updateWindowDimensions)
}
updateWindowDimensions () {
this.setState({ width: window.innerWidth, height: window.innerHeight })
}
唯一的区别是我在resize事件上deboundation(只运行每200ms一次)updateWindowDimensions来增加一点性能,但不是在ComponentDidMount上调用时deboundation它。
我发现,如果你经常安装它,那么反弹有时会使它的安装相当滞后。
只是一个小的优化,但希望它能帮助到别人!
使用React钩子:
你可以定义一个自定义钩子来监听窗口大小调整事件,就像这样:
import React, { useLayoutEffect, useState } from 'react';
function useWindowSize() {
const [size, setSize] = useState([0, 0]);
useLayoutEffect(() => {
function updateSize() {
setSize([window.innerWidth, window.innerHeight]);
}
window.addEventListener('resize', updateSize);
updateSize();
return () => window.removeEventListener('resize', updateSize);
}, []);
return size;
}
function ShowWindowDimensions(props) {
const [width, height] = useWindowSize();
return <span>Window size: {width} x {height}</span>;
}
这里的优点是逻辑是封装的,您可以在任何想要使用窗口大小的地方使用这个Hook。
使用React类:
你可以在componentDidMount中监听,类似于这个组件,它只显示窗口尺寸(如<span>窗口大小:1024 x 768</span>):
import React from 'react';
class ShowWindowDimensions extends React.Component {
state = { width: 0, height: 0 };
render() {
return <span>Window size: {this.state.width} x {this.state.height}</span>;
}
updateDimensions = () => {
this.setState({ width: window.innerWidth, height: window.innerHeight });
};
componentDidMount() {
window.addEventListener('resize', this.updateDimensions);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateDimensions);
}
}
@SophieAlpert是对的,+1,我只是想提供一个她的解决方案的修改版本,没有jQuery,基于这个答案。
var WindowDimensions = React.createClass({
render: function() {
return <span>{this.state.width} x {this.state.height}</span>;
},
updateDimensions: function() {
var w = window,
d = document,
documentElement = d.documentElement,
body = d.getElementsByTagName('body')[0],
width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight;
this.setState({width: width, height: height});
// if you are using ES2015 I'm pretty sure you can do this: this.setState({width, height});
},
componentWillMount: function() {
this.updateDimensions();
},
componentDidMount: function() {
window.addEventListener("resize", this.updateDimensions);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.updateDimensions);
}
});
2020年更新。对于认真关注性能的React开发人员。
上述解决方案确实有效,但只要窗口大小改变一个像素,就会重新渲染组件。
这通常会导致性能问题,所以我写了useWindowDimension钩子,在短时间内反弹调整大小事件。如100毫秒
import React, { useState, useEffect } from 'react';
export function useWindowDimension() {
const [dimension, setDimension] = useState([
window.innerWidth,
window.innerHeight,
]);
useEffect(() => {
const debouncedResizeHandler = debounce(() => {
console.log('***** debounced resize'); // See the cool difference in console
setDimension([window.innerWidth, window.innerHeight]);
}, 100); // 100ms
window.addEventListener('resize', debouncedResizeHandler);
return () => window.removeEventListener('resize', debouncedResizeHandler);
}, []); // Note this empty array. this effect should run only on mount and unmount
return dimension;
}
function debounce(fn, ms) {
let timer;
return _ => {
clearTimeout(timer);
timer = setTimeout(_ => {
timer = null;
fn.apply(this, arguments);
}, ms);
};
}
像这样使用它。
function YourComponent() {
const [width, height] = useWindowDimension();
return <>Window width: {width}, Window height: {height}</>;
}