开发者问题收集

React .scrollLeft 无法读取空值

2022-06-14
3176

我尝试使用 React 中的按钮访问水平滚动条,但结果发现它无法工作,因为无法读取 .scrollLeft 的空值。

<>
  <div className="containerOuterSider">
    <FaAngleLeft className= "FaAngleLeft" onClick={slide('left')}/>
    <div id="container3" className="container3">
      {products &&
        products
          .map((product) => (
            <AAsOfLowNav key={product._id} product={product} />
          ))
          .reverse()}
    </div>
      <FaAngleRight className="FaAngleRight" onClick={slide('right')}/>
  </div>
</>

主要功能是

function slide(direction) {
  var container = document.getElementById('container3');
  let scrollCompleted = 0;
  var slideVar = setInterval(function() {
    if (direction === 'left') {
      container.scrollLeft -= 10;
    } else {
      container.scrollLeft += 10;
    }
    scrollCompleted += 10;
    if (scrollCompleted >= 100) {
      window.clearInterval(slideVar);
    }
  }, 50);
}

我遇到的错误是:

Uncaught TypeError: Cannot read properties of null (reading 'scrollLeft') at LowerCatNav.js:31:1 Uncaught TypeError: Cannot read properties of null (reading 'scrollLeft') at LowerCatNav.js:33:1

我该如何做?我应该使用钩子还是其他什么?

2个回答

问题

您在渲染时立即调用 slide 函数:

<FaAngleLeft
  className="FaAngleLeft"
  onClick={slide('left')} // <-- immediately invoked when rendered
/>
...
<FaAngleRight
  className="FaAngleRight"
  onClick={slide('right')} // <-- immediately invoked when rendered
/>

React 组件尚未完全渲染并推送到 DOM,因此对 DOM 的查询(即 document.getElementById('container3') )返回 null

解决方案

修复点击处理程序,以便 slide 不会立即被调用。

<FaAngleLeft
  className="FaAngleLeft"
  onClick={() => slide('left')} // <-- asynchronously invoked when clicked
/>
...
<FaAngleRight
  className="FaAngleRight"
  onClick={() => slide('right')} // <-- asynchronously invoked when clicked
/>

直接查询 DOM 中的 DOMNodes 被视为 React 反模式,为此使用 React ref。

示例:

...

const containerRef = React.useRef(); // <-- (1) create Ref
const sliderTimerRef = React.useRef();

useEffect(() => {
  return () => {
    // clear any running intervals on component unmount
    clearInterval(sliderTimerRef.current);
  };
}, []);

...

function slide(direction) {
  // clear any previously set intervals and reset scrollCompleted
  clearInterval(sliderTimerRef.current);
  let scrollCompleted = 0;

  sliderTimerRef.current = setInterval(function() {
    const container = containerRef.current; // <-- (3) access current ref value

    if (direction === 'left') {
      container?.scrollLeft -= 10; // <-- (4)  Optional Chaining null check
    } else {
      container?.scrollLeft += 10; // <-- (4)  Optional Chaining null check
    }
    scrollCompleted += 10;
    if (scrollCompleted >= 100) {
      clearInterval(sliderTimerRef.current);
    }
  }, 50);
}

...

return (
  <>
    <div className="containerOuterSider">
      <FaAngleLeft className="FaAngleLeft" onClick={() => slide('left')}/>
      <div
        ref={containerRef} // <-- (2) attach ref to element
        id="container3"
        className="container3"
      >
        {products
          .map((product) => (
            <AAsOfLowNav key={product._id} product={product} />
          ))
          .reverse()
        }
      </div>
      <FaAngleRight className="FaAngleRight" onClick={() => slide('right')}/>
    </div>
  </>
);
Drew Reese
2022-06-14
  1. slide#container3 尚未创建时被调用。
    onClick={slide(...) 更改为 onClick={() => slide(...)

  2. 应避免访问全局元素(例如通过 id)。
    首选 ref / createRef / useRef 来访问属于您的组件的元素。

Ruslan Tushov
2022-06-14