Home > database >  The best way of the using useState hook in Function Component?
The best way of the using useState hook in Function Component?

Time:04-21

I want to show a div related to an object property that comes true or false. And I used a way but I'm not sure it's the best way or is it open the performance problems.

I'm checking that property in the loop that in return section for avoid extra array operation. But I think it will cause extra render.

The other option is checking that property in outside from return section. But this will be cause an extra array operation.

Which is the best way for me? I showed 2 different implementation below.

Option 1:

const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({ items, changeFilter }) => {
  const [showClearIcon, setShowClearIcon] = React.useState(false);
  
  return (
    <div className="radio-button-list">
      {showClearIcon && <div className="clear-icon">clear</div>}
      <ul>
        {items.map(item => {
          /* this is the area what I'm checking the property */
          if (item.selected) {
            setShowClearIcon(true);
          }
          return (
            <li key={item.id}>
              <label htmlFor={item.text} className="radio">
                <span className="input">
                  <input type="radio" onClick={changeFilter} readOnly />
                </span>
              </label>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

Option 2:

const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({ items, changeFilter }) => {
  const [showClearIcon, setShowClearIcon] = React.useState(false);


  /* set in useEffect hook */
  useEffect(() => {
    if(items.some(item => item.selected)) {
      setShowClearIcon(true);
    }
  }, [items]);
  
  return (
    <div className="radio-button-list">
      {showClearIcon && <div className="clear-icon">clear</div>}
      <ul>
        {items.map(item => {
          return (
            <li key={item.id}>
              <label htmlFor={item.text} className="radio">
                <span className="input">
                  <input type="radio" onClick={changeFilter} readOnly />
                </span>
              </label>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

CodePudding user response:

It looks like showClearIcon doesn't need to be a state atom at all, but just a memoized value dependent on items.

const showClearIcon = React.useMemo(
  () => items.some(item => item.selected), 
  [items],
);

CodePudding user response:

Option 1 enqueues a state update in the render return, don't use it.

Use option 2 to correctly enqueue the update as a side effect. In React the render function is to be considered a pure function. Don't unconditionally enqueue state updates.

Regarding performance, iterating an array is O(n). Iterating an array twice is still O(n).

Suggestion

The showClearIcon "state" probably shouldn't be React state since it's easily derived from the items prop.

Identify the Minimal (but complete) Representation of UI State

Let’s go through each one and figure out which one is state. Ask three questions about each piece of data:

  1. Is it passed in from a parent via props? If so, it probably isn’t state.
  2. Does it remain unchanged over time? If so, it probably isn’t state.
  3. Can you compute it based on any other state or props in your component? If so, it isn’t state.

Because of this, just compute the showClearIcon value locally.

const showClearIcon = items.some(item => item.selected);

This can be memoized with the useMemo hook with dependency on items if necessary.

CodePudding user response:

You can technically do it without useState and useEffect and without iterating over the array twice, check out the example below (it might not be neccessary but it's good to know that this is possible as well):

const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({
  items,
  changeFilter,
}) => {
  const renderItems = () => {
    let showClearIcon = false;
    let markup = (
      <ul>
        {items.map((item) => {
          if (item.selected) {
            showClearIcon = true;
          }
          return (
            <li key={item.id}>
              <label htmlFor={item.text} className="radio">
                <span className="input">
                  <input type="radio" onClick={changeFilter} readOnly />
                </span>
              </label>
            </li>
          );
        })}
      </ul>
    );

    return (
      <>
        {showClearIcon && <div className="clear-icon">clear</div>}
        {markup}
      </>
    );
  };

  return <div className="radio-button-list">{renderItems()}</div>;
};

Simply created a function that generates the markup.

CodePudding user response:

So, what you can do if you want to purely look at performance, is install the react dev tools chrome extension, then in your test server (for example localhost:3000) you can use the profiler tab in chrome's f12 menu to check which operation takes more memory!

this is something you can use for all optimization questions

CodePudding user response:

If your goal is to render html components, option 2 (useEffect) should be used, by keeping in mind that only the required dependencies are included in the dependency array (this is to avoid re-render and performance related issues). In your case, option 2 is correct, and its performance will be tuned by React itself.

CodePudding user response:

Option 2 is better approach, but Option 2 you can also have little better coding as below to make code more better and simpler

const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({ items, changeFilter }) => {
      // as suggested by AKX
      const showClearIcon = React.useMemo(
          () => items.some(item => item.selected), 
          [items],
       );
    
  /* set in useEffect hook */
  useEffect(() => {
    if(items.some(item => item.selected)) {
      setShowClearIcon(true);
    }
  }, [items]);

  // for displaying row items 
  const generateRow = ({id, text}) => {
    return (
      <li key={item.id}>
        <label htmlFor={item.text} className="radio">
          <span className="input">
            <input type="radio" onClick={changeFilter} readOnly />
          </span>
        </label>
      </li>
    );
  }
  
  return (
    <div className="radio-button-list">
      {showClearIcon && <div className="clear-icon">clear</div>}
      <ul>
        {items.map(item => generateRow(item))}
      </ul>
    </div>
  );
};
  • Related