0

i am trying to covert a small jquery component to react .but i am getting this issue Cannot read property 'offsetWidth' of null

here is jquery simple component http://jsfiddle.net/abhitalks/860LzgLL/

what component does it show "menu or tab list" .If window screen is decrease it hide menu and move to dropdown like this enter image description here

I tried to implement is react .

My approach is

  1. First i am calculate the width of li's or sum of all width of li.
  2. Also get the width of container.
  3. if sum of li width is greater I set the threshold . render using threshold value

but when I am resize my component I am getting below error

**Cannot read property 'offsetWidth' of null **

here is my code https://stackblitz.com/edit/react-wmtunp?file=src%2FApp.js

import React, { useState, useRef, createRef, useEffect } from 'react';
import './style.css';

const data = [
  'Option One',
  'Option_hellodummyhhhsdshshshd',
  'Option Three',
  'Option Four',
  'Option Five'
];

export default function App() {
  const [state, setState] = useState(data);
  const [threshold, setThreshold] = useState(-1);

  const liRefs = useRef([]);
  const ulRef = useRef(null);

  liRefs.current = state.map((_, i) => liRefs.current[i] ?? createRef());

  const adjustItems = function() {
    var skipLoop = false,
      w2W = ulRef.current.offsetWidth,
      index = -1,
      totalW = 0;
    liRefs.current.map(function(li, key) {
      if (skipLoop) {
        return;
      }
      totalW += li.current.offsetWidth;
      if (totalW > w2W) {
        skipLoop = true;
        index = key;
      }
      setThreshold(index);
    });
  };
  useEffect(() => {
    adjustItems();
    window.addEventListener('resize', adjustItems);
    return () => window.removeEventListener('resize', adjustItems);
  }, []);
  return (
    <div>
      <ul id="menu" ref={ulRef}>
        {state.map((i, index) =>
          threshold == -1 ? (
            <li ref={liRefs.current[index]} key={index}>
              {i}
            </li>
          ) : threshold !== -1 && threshold >= index ? (
            <li ref={liRefs.current[index]} key={index}>
              {i}
            </li>
          ) : (
            <></>
          )
        )}
      </ul>
      <ol>
        <li>
          Collected
          <ul id="submenu">
            {state.map((i, index) =>
              threshold !== -1 && threshold <= index ? (
                <li ref={liRefs.current[index]} key={index}>
                  {i}
                </li>
              ) : (
                <></>
              )
            )}
          </ul>
        </li>
      </ol>
    </div>
  );
}

My functionality break when I am resizing the window. I am new in react js .Is there problem in my approach ? any suggestion ?

user5711656
  • 3,310
  • 4
  • 33
  • 70

1 Answers1

1

Just try using ? this operator, since when you are resizing it might get null or undefined based on condition but by putting this condition it will help you put an "and" check if property value exists or not.

import React, { useState, useRef, createRef, useEffect } from 'react';
import './style.css';

const data = [
  'Option One',
  'Option_hellodummyhhhsdshshshd',
  'Option Three',
  'Option Four',
  'Option Five'
];

export default function App() {
  const [state, setState] = useState(data);
  const [threshold, setThreshold] = useState(-1);

  const liRefs = useRef([]);
  const ulRef = useRef(null);

  liRefs.current = state.map((_, i) => liRefs.current[i] ?? createRef());

  const adjustItems = function() {
    let skipLoop = false,
      w2W = ulRef.current.offsetWidth,
      totalW = 0;
    liRefs.current.map((li, key) => {
      if (skipLoop) {
        return;
      }
      totalW += li?.current?.offsetWidth;
      if (totalW > w2W) {
        skipLoop = true;
        setThreshold(key);
      } else {
        setThreshold(liRefs.current.length);
      }
    });
  };

  useEffect(() => {
    adjustItems();
    window.addEventListener('resize', adjustItems);
    return () => window.removeEventListener('resize', adjustItems);
  }, []);

  return (
    <div>
      <ul id="menu" ref={ulRef}>
        {state.map((i, index) =>
          threshold == -1 ? (
            <li ref={liRefs.current[index]} key={index}>
              {i}
            </li>
          ) : threshold !== -1 && threshold > index ? (
            <li ref={liRefs.current[index]} key={index}>
              {i}
            </li>
          ) : (
            <></>
          )
        )}
      </ul>
      <ol>
        <li>
          Collected
          <ul id="submenu">
            {state.map((i, index) =>
              threshold !== -1 && threshold <= index ? (
                <li ref={liRefs.current[index]} key={index}>
                  {i}
                </li>
              ) : (
                <></>
              )
            )}
          </ul>
        </li>
      </ol>
    </div>
  );
}

You can reference it https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

Divya
  • 121
  • 6
  • actually bug is resolve but not working as expected as jsfiddle .is my approach correct ? – user5711656 Jul 03 '21 at 14:08
  • Could you please help me out with your exact problem statement like what exactly you are trying to build? – Divya Jul 03 '21 at 14:11
  • I am trying to implement this http://jsfiddle.net/abhitalks/860LzgLL/ in react js – user5711656 Jul 03 '21 at 14:13
  • In other words show all `li`s if space is available .else move to dropdown or below more link – user5711656 Jul 03 '21 at 14:14
  • actually when I am resizing my window my `menu` options are not going below `more option` or dropdown – user5711656 Jul 03 '21 at 14:15
  • I have updated the code, just updated the adjustItems function where you are setting the threshold value. Added an else condition to that with certain modification. – Divya Jul 03 '21 at 15:27