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

时间:2017-02-02 20:54:59

标签: javascript reactjs

基本上我希望能够检测反应组件是否有溢出的子项。就像在this question中一样。我发现使用ReactDOM可以做同样的事情,但是i cannot/should not use ReactDOM。我没有在建议的替代方案ref上看到任何相同内容。

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

5 个答案:

答案 0 :(得分:9)

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

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

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

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

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

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

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;
},

答案 1 :(得分:3)

是的,您可以使用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>
  }
})

MyComponent挂载时,它会调用将ref设置为DOM元素this.domElement的{​​{1}}函数。

有了这个,使用像#my-component这样的东西来渲染DOM元素并确定子元素是否溢出父元素是相当容易的:

https://jsbin.com/lexonoyamu/edit?js,console,output

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

答案 2 :(得分:1)

对于任何想知道如何使用钩子和useRef做到这一点的人:

// This is custom effect that calls onResize when page load and on window resize
const useResizeEffect = (onResize, deps = []) => {
  useEffect(() => {
    onResize();
    window.addEventListener("resize", onResize);

    return () => window.removeEventListener("resize", onResize);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [...deps, onResize]);
};

const App = () => {
  const [isScrollable, setIsScrollable] = useState(false);
  const [container, setContainer] = useState(null);
  // this has to be done by ref so when window event resize listener will trigger - we will get the current element
  const containerRef = useRef(container);
  containerRef.current = container;
  const setScrollableOnResize = useCallback(() => {
    if (!containerRef.current) return;
    const { clientWidth, scrollWidth } = containerRef.current;
    setIsScrollable(scrollWidth > clientWidth);
  }, [containerRef]);
  useResizeEffect(setScrollableOnResize, [containerRef]);

  return (
    <div
      className={"container" + (isScrollable ? " scrollable" : "")}
      ref={(element) => {
        if (!element) return;
        setContainer(element);
        const { clientWidth, scrollWidth } = element;
        setIsScrollable(scrollWidth > clientWidth);
      }}
    >
      <div className="content">
        <div>some conetnt</div>
      </div>
    </div>
  );
};

Edit bv7q6

答案 3 :(得分:0)

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

Edit 1o0k7vr1m3

答案 4 :(得分:0)

同样可以使用 React 钩子来实现:

你需要的第一件事是一个状态,它包含 text openoverflow active 的布尔值:

const [textOpen, setTextOpen] = useState(false);
const [overflowActive, setOverflowActive] = useState(false);

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

const textRef = useRef();
<p ref={textRef}>
    Some huuuuge text
</p>

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

function isOverflowActive(event) {
    return event.offsetHeight < event.scrollHeight || e.offsetWidth < e.scrollWidth;
}

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

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

    setOverflowActive(false);
}, [isOverflowActive]);

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

{!textOpen && !overflowActive ? null : (
    <button>{textOpen ? 'Show less' : 'Show more'}</button>
)}