Javascript 如何响应React中自动调整大小的DOM元素的宽度?

Javascript 如何响应React中自动调整大小的DOM元素的宽度?,javascript,reactjs,responsive,Javascript,Reactjs,Responsive,我有一个使用React组件的复杂网页,我正在尝试将该网页从静态布局转换为更具响应性、可调整大小的布局。然而,我在React中不断遇到一些限制,我想知道是否有一种处理这些问题的标准模式。在我的特定情况下,我有一个组件,它以div的形式呈现,display:table cell和width:auto 不幸的是,我无法查询组件的宽度,因为您无法计算元素的大小,除非它实际放置在DOM中(DOM具有用于推断实际渲染宽度的完整上下文)。除了将其用于相对鼠标定位之类的事情之外,我还需要它来正确设置组件中SVG

我有一个使用React组件的复杂网页,我正在尝试将该网页从静态布局转换为更具响应性、可调整大小的布局。然而,我在React中不断遇到一些限制,我想知道是否有一种处理这些问题的标准模式。在我的特定情况下,我有一个组件,它以div的形式呈现,display:table cell和width:auto

不幸的是,我无法查询组件的宽度,因为您无法计算元素的大小,除非它实际放置在DOM中(DOM具有用于推断实际渲染宽度的完整上下文)。除了将其用于相对鼠标定位之类的事情之外,我还需要它来正确设置组件中SVG元素的宽度属性

此外,当窗口调整大小时,如何在设置过程中将尺寸变化从一个组件传递到另一个组件?我们正在shouldComponentUpdate中进行所有第三方SVG渲染,但您不能在该方法中设置自己或其他子组件的状态或属性


使用React是否有处理此问题的标准方法?

我认为您正在寻找的生命周期方法是
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" />
  }

});
var Container=React.createComponent({
componentDidMount:函数(){
//如果使用React<0.14,请使用此.refs.svg.getDOMNode().offsetWidth
var width=this.refs.svg.offsetWidth;
},
渲染:函数(){
}
});

除了couchand解决方案,您还可以使用findDOMNode

var Container = React.createComponent({

  componentDidMount: function () {
    var width = React.findDOMNode(this).offsetWidth;
  },

  render: function () {
    <svg />
  }
});
var Container=React.createComponent({
componentDidMount:函数(){
var width=React.findDOMNode(this.offsetWidth);
},
渲染:函数(){
}
});

最实用的解决方案是使用库来处理这类问题

更新:现在有一个用于调整大小检测的自定义钩子(我个人没有尝试过):。作为一个自定义钩子,它看起来比
react-measure
更方便使用

import * as React from 'react'
import Measure from 'react-measure'

const MeasuredComp = () => (
  <Measure bounds>
    {({ measureRef, contentRect: { bounds: { width }} }) => (
      <div ref={measureRef}>My width is {width}</div>
    )}
  </Measure>
)
如果您真的愿意,如何推出您自己的:

创建一个包装器组件,用于处理从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)}/>;
  }
}
/@flow
从“React”导入React,{Component};
从“shallowEqual”进口shallowEqual;
从“lodash.throttle”进口节流阀;
类型DefaultProps={
组件:ReactClass,
};
类型道具={
domProps?:数组,
computedStyleProps?:数组,
子项:(state:state)=>?React.Element,
组件:ReactClass,
};
类型状态={
重新测量:()=>无效,
computedStyle?:对象,
[domProp:string]:任何,
};
导出默认类响应扩展组件{
静态defaultProps={
组件:'div',
};
重新测量:()=>void=节流阀(()=>{
const{root}=这个;
如果(!root)返回;
const{domProps,computedStyleProps}=this.props;
const nextState:$Shape={};
if(domProps)domProps.forEach(prop=>nextState[prop]=root[prop]);
if(计算样式道具){
nextState.computedStyle={};
const computedStyle=getComputedStyle(根);
computedStyleProps.forEach(prop=>
nextState.computedStyle[prop]=computedStyle[prop]
);
}
此设置状态(下一个设置状态);
}, 500);
//将重新测量置于状态,以便传递给孩子
//函数以及computedStyle和domProps
state:state={remeasure:this.remeasure};
根:?对象;
componentDidMount(){
这个。重新测量();
这个。重新测量。冲洗();
window.addEventListener('resize',this.remeasure);
}
组件将接收道具(下一步:道具){
如果(!shallewequal(this.props.domProps,nextrops.domProps)|
!shallowEqual(this.props.computedStyleProps,nextProps.computedStyleProps)){
这个。重新测量();
}
}
组件将卸载(){
这个。重新测量。取消();
window.removeEventListener('resize',this.remeasure);
}
render():?React.Element{
const{props:{children,Comp},state}=this;
返回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>
);
函数renderColumns(numColumns:number):React.Element{
...
}
常量响应视图=(
{({offsetWidth}:{offsetWidth:number}):?React.Element=>{
如果(!offsetWidth)返回null;
常量numColumns=数学最大值(1,数学楼层(偏移网络宽度/200));
返回renderColumns(numColumns);
}}
);

您可以使用我编写的I库,该库监视组件的渲染大小并将其传递给您

例如:

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);   
从“react SizeMe”导入SizeMe;
类MySVG扩展组件{
render(){
//“我的库”会将尺寸道具传递到组件中。
const{width,height}=this.props.size;
返回(
);
}
} 
//用我的库包装组件导出。
导出默认的SizeMe()(MySVG);

演示:

Github:


它使用了一种优化的滚动/基于对象的算法,我从比我聪明得多的人那里借来的。:)

澄清一下:在React=0.13.x中,使用React.findDOMNode()@pxwise aaaaa,现在对于DOM元素引用,您甚至不必在React 0.14:)@pxwise中使用任何一个函数,我相信这是
ReactDOM.findDOMNode()
now?@MichaelScheper是的,有一些错误
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);