当浏览器窗口调整大小时,如何让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”的方式来做到这一点?
Edit 2018:现在React拥有对上下文的一流支持
我将尝试给出一个一般的答案,针对这个特定的问题,但也针对一个更普遍的问题。
如果您不关心副作用库,您可以简单地使用Packery之类的东西
如果你使用Flux,你可以创建一个包含窗口属性的存储,这样你就可以保持一个纯粹的呈现函数,而不必每次都查询窗口对象。
在其他情况下,你想建立一个响应式网站,但你更喜欢React内联样式的媒体查询,或者希望HTML/JS行为根据窗口宽度改变,请继续阅读:
什么是React上下文?为什么我要谈论它
React上下文不在公共API中,允许将属性传递给整个组件层次结构。
React上下文特别有用,它可以传递给你整个应用程序中永远不会改变的东西(许多Flux框架通过mixin使用它)。你可以用它来存储应用程序业务不变量(比如连接的userId,这样它就可以在任何地方使用)。
但它也可以用来存储可以改变的东西。问题是,当上下文改变时,所有使用它的组件都应该重新呈现,这并不容易做到,最好的解决方案通常是卸载/重新挂载整个应用程序与新的上下文。记住forceUpdate不是递归的。
因此,正如您所理解的,上下文是实用的,但是当它改变时,会对性能产生影响,所以它不应该经常改变。
把什么放在上下文中
不变量:比如连接的userId, sessionToken,等等…
不经常改变的东西
以下是一些不会经常改变的东西:
当前用户语言:
它不会经常改变,当它改变时,整个应用程序都被翻译了,我们必须重新渲染所有内容:这是热语言变化的一个非常好的用例
窗口属性
宽度和高度不会经常改变,但当我们这样做时,我们的布局和行为可能不得不适应。对于布局,有时很容易使用CSS mediaqueries进行自定义,但有时不是,需要不同的HTML结构。对于行为,你必须用Javascript来处理。
你不希望在每个调整大小事件上重新渲染所有内容,所以你必须撤消调整大小事件。
我对你的问题的理解是,你想知道根据屏幕宽度显示多少项。因此,您必须首先定义响应式断点,并枚举可以拥有的不同布局类型的数量。
例如:
布局“1col”,宽度<= 600
布局为“2col”,用于600 < width < 1000
布局为“3col”,用于1000 <= width
在调整大小事件(debpublished)上,您可以通过查询窗口对象轻松获得当前布局类型。
然后你可以比较布局类型与以前的布局类型,如果它已经改变了,重新呈现应用程序与一个新的上下文:这允许避免重新呈现应用程序在用户有触发调整大小事件,但实际上布局类型没有改变,所以你只在需要时重新呈现。
一旦你有了它,你就可以简单地在你的应用程序中使用布局类型(通过上下文访问),这样你就可以自定义HTML、行为、CSS类……你知道你的布局类型在React渲染函数中,所以这意味着你可以通过使用内联样式安全地编写响应式网站,而根本不需要介质查询。
如果你使用Flux,你可以使用一个商店而不是React上下文,但如果你的应用程序有很多响应组件,也许使用上下文更简单?
@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);
}
});