如何在React中响应自动调整大小的DOM元素的宽度? [英] How can I respond to the width of an auto-sized DOM element in React?

查看:1551
本文介绍了如何在React中响应自动调整大小的DOM元素的宽度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用React组件的复杂网页,我正在尝试将页面从静态布局转换为响应更快,可调整大小的布局。但是,我一直遇到React的限制,我想知道是否有处理这些问题的标准模式。在我的特定情况下,我有一个组件,显示为带显示的div:table-cell和width:auto。

I have a complex web page using React components, and am trying to convert the page from a static layout to a more responsive, resizable layout. However, I keep running into limitations with React, and am wondering if there's a standard pattern for handling these issues. In my specific case, I have a component that renders as a div with display:table-cell and width:auto.

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

Unfortunately, I cannot query the width of my component, because you can't compute the size of an element unless it's actually placed in the DOM (which has the full context with which to deduce the actual rendered width). Besides using this for things like relative mouse positioning, I also need this to properly set width attributes on SVG elements within the component.

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

In addition, when the window resizes, how do I communicate size changes from one component to another during setup? We're doing all of our 3rd-party SVG rendering in shouldComponentUpdate, but you cannot set state or properties on yourself or other child components within that method.

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

Is there a standard way of dealing with this problem using React?

推荐答案

最实用的解决方案是使用 react-measure

The most practical solution is to use react-measure.

注意:由于API已更改,此代码不适用于 react-measure@^2.0.0 。请访问上面的链接以查看新API。

Note: this code does not work with react-measure@^2.0.0 as the API has changed. Visit the above link to see the new API.

import Measure from 'react-measure'

const MeasuredComp = () => (
  <Measure>
    {({width}) => <div>My width is {width}</div>}
  </Measure>
)

要传达组件之间的大小更改,您可以传递 onMeasure 回调并存储它在某处收到的值(这些天分享状态的标准方法是使用 Redux ):

To communicate size changes between components, you can pass an onMeasure callback and store the values it receives somewhere (the standard way of sharing state these days is to use 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>
))

如果您真的愿意,如何自己动手:

创建一个包装器组件,用于处理从DOM获取值并监听窗口大小调整事件(或使用的组件调整大小检测-measure )。你告诉它从DOM获取哪些道具,并提供一个渲染函数,将这些道具作为一个孩子。

Create a wrapper component that handles getting values from the DOM and listening to window resize events (or component resize detection as used by react-measure). You tell it which props to get from the DOM and provide a render function taking those props as a child.

你渲染的东西必须在DOM道具之前安装读;当这些道具在初始渲染期间不可用时,您可能希望使用 style = {{visibility:'hidden'}} ,以便用户无法看到它在获得JS计算布局之前。

What you render has to get mounted before the DOM props can be read; when those props aren't available during the initial render, you might want to use style={{visibility: 'hidden'}} so that the user can't see it before it gets a JS-computed layout.

// @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)}/>;
  }
}

有了这个,响应宽度变化非常简单:

With this, responding to width changes is very simple:

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>
);

这篇关于如何在React中响应自动调整大小的DOM元素的宽度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆