浏览器上的Rerender视图使用React调整大小
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浏览器上的Rerender视图使用React调整大小相关的知识,希望对你有一定的参考价值。
在调整浏览器窗口大小时,如何让React重新渲染视图?
Background
我有一些块,我想在页面上单独布局,但我也希望它们在浏览器窗口更改时更新。最终结果将类似于Ben Holland's 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>
);
}
});
和Blocks
的列表/集合:
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>
);
}
});
Question
我应该添加jQuery窗口调整大小?如果是的话,在哪里?
$( window ).resize(function() {
// re-render the component
});
有没有更“反应”的方式这样做?
你可以在componentDidMount中监听,就像这个只显示窗口尺寸的组件一样(如<span>1024 x 768</span>
):
var WindowDimensions = React.createClass({
render: function() {
return <span>{this.state.width} x {this.state.height}</span>;
},
updateDimensions: function() {
this.setState({width: $(window).width(), height: $(window).height()});
},
componentWillMount: function() {
this.updateDimensions();
},
componentDidMount: function() {
window.addEventListener("resize", this.updateDimensions);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.updateDimensions);
}
});
此代码使用新的React context API:
import React, { PureComponent, createContext } from 'react';
const { Provider, Consumer } = createContext({ width: 0, height: 0 });
class WindowProvider extends PureComponent {
state = this.getDimensions();
componentDidMount() {
window.addEventListener('resize', this.updateDimensions);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateDimensions);
}
getDimensions() {
const w = window;
const d = document;
const documentElement = d.documentElement;
const body = d.getElementsByTagName('body')[0];
const width = w.innerWidth || documentElement.clientWidth || body.clientWidth;
const height = w.innerHeight || documentElement.clientHeight || body.clientHeight;
return { width, height };
}
updateDimensions = () => {
this.setState(this.getDimensions());
};
render() {
return <Provider value={this.state}>{this.props.children}</Provider>;
}
}
然后你可以在代码中的任何地方使用它,如下所示:
<WindowConsumer>
{({ width, height }) => //do what you want}
</WindowConsumer>
不确定这是否是最好的方法,但对我来说最有效的是创建一个商店,我称之为WindowStore:
import {assign, events} from '../../libs';
import Dispatcher from '../dispatcher';
import Constants from '../constants';
let CHANGE_EVENT = 'change';
let defaults = () => {
return {
name: 'window',
width: undefined,
height: undefined,
bps: {
1: 400,
2: 600,
3: 800,
4: 1000,
5: 1200,
6: 1400
}
};
};
let save = function(object, key, value) {
// Save within storage
if(object) {
object[key] = value;
}
// Persist to local storage
sessionStorage[storage.name] = JSON.stringify(storage);
};
let storage;
let Store = assign({}, events.EventEmitter.prototype, {
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
window.addEventListener('resize', () => {
this.updateDimensions();
this.emitChange();
});
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
get: function(keys) {
let value = storage;
for(let key in keys) {
value = value[keys[key]];
}
return value;
},
initialize: function() {
// Set defaults
storage = defaults();
save();
this.updateDimensions();
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
window.removeEventListener('resize', () => {
this.updateDimensions();
this.emitChange();
});
},
updateDimensions: function() {
storage.width =
window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
storage.height =
window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight;
save();
}
});
export default Store;
然后我在我的组件中使用了那个商店,有点像这样:
import WindowStore from '../stores/window';
let getState = () => {
return {
windowWidth: WindowStore.get(['width']),
windowBps: WindowStore.get(['bps'])
};
};
export default React.createClass(assign({}, base, {
getInitialState: function() {
WindowStore.initialize();
return getState();
},
componentDidMount: function() {
WindowStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
WindowStore.removeChangeListener(this._onChange);
},
render: function() {
if(this.state.windowWidth < this.state.windowBps[2] - 1) {
// do something
}
// return
return something;
},
_onChange: function() {
this.setState(getState());
}
}));
仅供参考,这些文件被部分修剪。
我知道这已经得到了回答,但我只是认为我会分享我的解决方案作为最佳答案,虽然很棒,现在可能有点过时了。
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事件上对updateWindowDimensions进行了debouncing(仅运行每200ms)以稍微提高性能,但是当它在ComponentDidMount上调用时没有对它进行去抖动。
我发现如果你有经常安装的情况,有时会发动去抖动使得它很容易安装。
只是一个小小的优化,但希望它有助于某人!
componentDidMount() {
// Handle resize
window.addEventListener('resize', this.handleResize);
}
handleResize = () => {
this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight);
this.camera.aspect = this.mount.clientWidth / this.mount.clientHeight;
this.camera.updateProjectionMatrix();
};
只需要定义resize事件功能。
然后更新渲染器大小(画布),为摄像机分配新的宽高比。
在我看来,卸载和重新安装是一个疯狂的解决方案....
如果需要,下面是山。
<div
className={this.state.canvasActive ? 'canvasContainer isActive' : 'canvasContainer'}
ref={mount => {
this.mount = mount;
}}
/>
谢谢大家的答案。这是我的React + Recompose。它是一个高阶函数,包括组件的windowHeight
和windowWidth
属性。
const withDimensions = compose(
withStateHandlers(
({
windowHeight,
windowWidth
}) => ({
windowHeight: window.innerHeight,
windowWidth: window.innerWidth
}), {
handleResize: () => () => ({
windowHeight: window.innerHeight,
windowWidth: window.innerWidth
})
}),
lifecycle({
componentDidMount() {
window.addEventListener('resize', this.props.handleResize);
},
componentWillUnmount() {
window.removeEventListener('resize');
}})
)
https://github.com/renatorib/react-sizes是一个HOC来做这个,同时仍然保持良好的表现。
import React from 'react'
import withSizes from 'react-sizes'
@withSizes(({ width }) => ({ isMobile: width < 480 }))
class MyComponent extends Component {
render() {
return <div>{this.props.isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
}
}
export default MyC以上是关于浏览器上的Rerender视图使用React调整大小的主要内容,如果未能解决你的问题,请参考以下文章
IOS 上的 React-native:无法调整当前堆栈顶部超出可用视图
React Rerender 组件不起作用,无法读取未定义的属性“forceUpdate”[重复]
根据我在 rails/react 应用程序上的 api 调用状态更新我的视图