如何响应React中自动resize的DOM元素的宽度?
我有一个使用React组件的复杂网页,并试图将页面从静态布局转换为更具响应性的可resize的布局。 然而,我仍然遇到了React的限制,我想知道是否有一个标准的模式来处理这些问题。 在我的具体情况下,我有一个组件呈现为与显示的div:table-cell和width:auto。
不幸的是,我不能查询我的组件的宽度,因为你不能计算一个元素的大小,除非它实际上被放置在DOM(具有推断实际渲染宽度的完整上下文)中。 除了像鼠标定位这样的东西外,我还需要在组件中正确设置SVG元素的宽度属性。
此外,当窗口resize时,如何在安装过程中将尺寸更改从一个组件传递到另一个组件? 我们在shouldComponentUpdate中完成所有第三方的SVG渲染,但不能在你的方法中设置自己或其他子组件的状态或属性。
有没有使用React处理这个问题的标准方法?
最实际的解决scheme是使用反应措施 。
注意 :这个代码在API改变时不能与react-measure@^2.0.0
一起使用。 访问上面的链接来查看新的API。
import Measure from 'react-measure' const MeasuredComp = () => ( <Measure> {({width}) => <div>My width is {width}</div>} </Measure> )
要在组件之间传递大小更改,可以传递一个onMeasure
callback函数,并将其接收到的值存储(目前,共享状态的标准方式是使用Redux ):
import Measure from 'react-measure' import connect from 'react-redux' import {setMyCompWidth} from './actions' // some action that stores width in somewhere in redux state function select(state) { return { currentWidth: ... // get width from somewhere in the state } } const MyComp = connect(select)(({dispatch, currentWidth}) => ( <Measure onMeasure={({width}) => dispatch(setMyCompWidth(width))}> <div>MyComp width is {currentWidth}</div> </Measure> ))
如果你真的想要:
创build一个包装组件,该组件处理从DOM获取值并侦听窗口大小调整事件(或react-measure
使用的组件大小调整检测)。 你告诉它从DOM获得哪些道具,并提供一个把这些道具作为孩子的渲染函数。
在DOM道具可以被读取之前,你渲染的东西必须被挂载; 当这些道具在初始渲染过程中不可用时,您可能需要使用style={{visibility: 'hidden'}}
以便用户在获得JS计算的布局之前无法看到它。
// @flow import React, {Component} from 'react'; import shallowEqual from 'shallowequal'; import throttle from 'lodash.throttle'; type DefaultProps = { component: ReactClass<any>, }; type Props = { domProps?: Array<string>, computedStyleProps?: Array<string>, children: (state: State) => ?React.Element<any>, component: ReactClass<any>, }; type State = { remeasure: () => void, computedStyle?: Object, [domProp: string]: any, }; export default class Responsive extends Component<DefaultProps,Props,State> { static defaultProps = { component: 'div', }; remeasure: () => void = throttle(() => { const {root} = this; if (!root) return; const {domProps, computedStyleProps} = this.props; const nextState: $Shape<State> = {}; if (domProps) domProps.forEach(prop => nextState[prop] = root[prop]); if (computedStyleProps) { nextState.computedStyle = {}; const computedStyle = getComputedStyle(root); computedStyleProps.forEach(prop => nextState.computedStyle[prop] = computedStyle[prop] ); } this.setState(nextState); }, 500); // put remeasure in state just so that it gets passed to child // function along with computedStyle and domProps state: State = {remeasure: this.remeasure}; root: ?Object; componentDidMount() { this.remeasure(); this.remeasure.flush(); window.addEventListener('resize', this.remeasure); } componentWillReceiveProps(nextProps: Props) { if (!shallowEqual(this.props.domProps, nextProps.domProps) || !shallowEqual(this.props.computedStyleProps, nextProps.computedStyleProps)) { this.remeasure(); } } componentWillUnmount() { this.remeasure.cancel(); window.removeEventListener('resize', this.remeasure); } render(): ?React.Element<any> { const {props: {children, component: Comp}, state} = this; return <Comp ref={c => this.root = c} children={children(state)}/>; } }
有了这个,对宽度变化的响应非常简单:
function renderColumns(numColumns: number): React.Element<any> { ... } const responsiveView = ( <Responsive domProps={['offsetWidth']}> {({offsetWidth}: {offsetWidth: number}): ?React.Element<any> => { if (!offsetWidth) return null; const numColumns = Math.max(1, Math.floor(offsetWidth / 200)); return renderColumns(numColumns); }} </Responsive> );
我认为你正在寻找的生命周期方法是componentDidMount
。 这些元素已经放置在DOM中,您可以从组件的refs
获取有关它们的信息。
例如:
var Container = React.createComponent({ componentDidMount: function () { // if using React < 0.14, use this.refs.svg.getDOMNode().offsetWidth var width = this.refs.svg.offsetWidth; }, render: function () { <svg ref="svg" /> } });
您也可以使用findDOMNode替代couchand解决scheme
var Container = React.createComponent({ componentDidMount: function () { var width = React.findDOMNode(this).offsetWidth; }, render: function () { <svg /> } });
你可以使用我编写的库监视你的组件的渲染大小,并传递给你。
例如:
import SizeMe from 'react-sizeme'; class MySVG extends Component { render() { // A size prop is passed into your component by my library. const { width, height } = this.props.size; return ( <svg width="100" height="100"> <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /> </svg> ); } } // Wrap your component export with my library. export default SizeMe()(MySVG);
演示: https : //react-sizeme-example-esbefmsitg.now.sh/
Github: https : //github.com/ctrlplusb/react-sizeme
它使用了一个优化的滚动/基于对象的algorithm,我从人们那里借来的比我聪明得多。 🙂