如何在没有ReactDOM的情况下检测React组件的溢出?

Jem*_*nes 3 javascript reactjs

基本上我希望能够检测反应组件是否有溢出的子项.就像在这个问题中一样.我发现使用ReactDOM可以做同样的事情,但我不能/不应该使用ReactDOM.我没有看到建议的替代方案ref,这是等效的.

所以我需要知道的是,在这些条件下是否有可能检测到反应组分内的溢出.同样,是否可以检测宽度?

Jem*_*nes 11

除了@ jered的优秀答案之外,我还想提一下限定符,如果ref 直接放在DOM元素上,ref只会返回一个直接访问常规DOM元素的各种属性的元素.也就是说,它不会以这种方式与组件一起运行.

所以,如果你像我一样,并拥有以下内容:

var MyComponent = React.createClass({
  render: function(){
    return <SomeComponent id="my-component" ref={(el) => {this.element = el}}/>
  }
})
Run Code Online (Sandbox Code Playgroud)

当您尝试访问this.element(可能在componentDidMount或中componentDidUpdate)的DOM属性并且您没有看到所述属性时,以下可能是适合您的替代方案

var MyComponent = React.createClass({
  render: function(){
    return <div ref={(el) => {this.element = el}}>
             <SomeComponent id="my-component"/>
          </div>
  }
})
Run Code Online (Sandbox Code Playgroud)

现在您可以执行以下操作:

componentDidUpdate() {
  const element = this.element;
  // Things involving accessing DOM properties on element
  // In the case of what this question actually asks:
  const hasOverflowingChildren = element.offsetHeight < element.scrollHeight ||
                                 element.offsetWidth < element.scrollWidth;
},
Run Code Online (Sandbox Code Playgroud)


Rin*_*nor 11

@Jemar Jones 提出的解决方案的实现:

export default class OverflowText extends Component {
  constructor(props) {
    super(props);
    this.state = {
      overflowActive: false
    };
  }

  isEllipsisActive(e) {
    return e.offsetHeight < e.scrollHeight || e.offsetWidth < e.scrollWidth;
  }

  componentDidMount() {
    this.setState({ overflowActive: this.isEllipsisActive(this.span) });
  }

  render() {
    return (
      <div
        style={{
          width: "145px",
          textOverflow: "ellipsis",
          whiteSpace: "nowrap",
          overflow: "hidden"
        }}
        ref={ref => (this.span = ref)}
      >
        <div>{"Triggered: " + this.state.overflowActive}</div>
        <span>This is a long text that activates ellipsis</span>
      </div>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑 1o0k7vr1m3

  • 感谢您的工作代码示例。我发现我还需要从 componentDidUpdate 运行 isEllipsisActive,为了避免无限循环,我需要在更新状态之前添加一个检查: if (this.isEllipsisActive(this.span) !== this.state.overflowActive) { //在这里更新状态 } (3认同)

小智 11

我需要在 React TypeScript 中实现这一点,因此这里是使用 React Hooks 在 TypeScript 中更新的解决方案。如果至少有 4 行文本,此解决方案将返回 true。

我们声明必要的状态变量:

  const [overflowActive, setOverflowActive] = useState<boolean>(false);
  const [showMore, setShowMore] = useState<boolean>(false);
Run Code Online (Sandbox Code Playgroud)

我们使用以下方法声明必要的引用useRef

  const overflowingText = useRef<HTMLSpanElement | null>(null);
Run Code Online (Sandbox Code Playgroud)

我们创建一个检查溢出的函数:

  const checkOverflow = (textContainer: HTMLSpanElement | null): boolean => {
    if (textContainer)
      return (
        textContainer.offsetHeight < textContainer.scrollHeight || textContainer.offsetWidth < textContainer.scrollWidth
      );
    return false;
  };
Run Code Online (Sandbox Code Playgroud)

让我们构建一个在更改useEffect时调用的函数overflowActive,并检查当前的 ref 对象以确定该对象是否溢出:

  useEffect(() => {
    if (checkOverflow(overflowingText.current)) {
      setOverflowActive(true);
      return;
    }

    setOverflowActive(false);
  }, [overflowActive]);
Run Code Online (Sandbox Code Playgroud)

在组件的 return 语句中,我们需要将 ref 绑定到适当的元素。我正在使用Material UI耦合,styled-components因此本示例中的元素将是StyledTypography

<StyledTypography ref={overflowingText}>{message}</StyledTypography>
Run Code Online (Sandbox Code Playgroud)

设计组件的样式styled-components

const StyledTypography = styled(Typography)({
  display: '-webkit-box',
  '-webkit-line-clamp': '4',
  '-webkit-box-orient': 'vertical',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
});
Run Code Online (Sandbox Code Playgroud)


小智 9

使用 React hooks 也可以实现同样的效果:

您需要的第一件事是保存文本打开溢出活动的布尔值的状态:

const [textOpen, setTextOpen] = useState(false);
const [overflowActive, setOverflowActive] = useState(false);
Run Code Online (Sandbox Code Playgroud)

接下来,您需要对要检查溢出的元素进行引用:

const textRef = useRef();
<p ref={textRef}>
    Some huuuuge text
</p>
Run Code Online (Sandbox Code Playgroud)

接下来是一个检查元素是否溢出的函数:

function isOverflowActive(event) {
    return event.offsetHeight < event.scrollHeight || event.offsetWidth < event.scrollWidth;
}
Run Code Online (Sandbox Code Playgroud)

然后你需要一个 useEffect 钩子来检查上述函数是否存在溢出:

useEffect(() => {
    if (isOverflowActive(textRef.current)) {
        setOverflowActive(true);
        return;
    }

    setOverflowActive(false);
}, [isOverflowActive]);
Run Code Online (Sandbox Code Playgroud)

现在有了这两种状态和一个检查溢出元素是否存在的函数,您可以有条件地渲染某些元素(例如,显示更多按钮):

{!textOpen && !overflowActive ? null : (
    <button>{textOpen ? 'Show less' : 'Show more'}</button>
)}
Run Code Online (Sandbox Code Playgroud)


jer*_*red 5

是的,您可以使用ref

ref在官方文档中了解有关工作原理的更多信息:https : //facebook.github.io/react/docs/refs-and-the-dom.html

基本上,ref这只是在组件刚被调用之前第一次渲染时运行的回调componentDidMount。回调中的参数是正在调用该ref函数的DOM元素。因此,如果您有这样的事情:

var MyComponent = React.createClass({
  render: function(){
    return <div id="my-component" ref={(el) => {this.domElement = el}}>Hello World</div>
  }
})
Run Code Online (Sandbox Code Playgroud)

MyComponent坐骑,它会调用该ref函数集this.domElement的DOM元素#my-component

有了它,使用诸如getBoundingClientRect()DOM元素渲染后确定子元素是否溢出父元素的方法就很容易了:

https://jsbin.com/lexonoyamu/edit?js,控制台,输出

请记住,在定义DOM元素之前,无法测量它们的大小/溢出,因为根据定义,它们尚不存在。您无法测量某物的宽度/高度,除非将其渲染到屏幕上。