Skip to content Skip to sidebar Skip to footer

Usage Of Usecallback And Setting New Object State Using Previous State As Argument

Consider this basic form fields component with a custom form hook to handle input changes: import React, { useState, useCallback } from 'react'; const useFormInputs = (initialSta

Solution 1:

Your first question:

In your case it doesn't matter because everything is rendered in the same component. If you have a list of things that get an event handler then useCallback can save you some renders.

In the example below the first 2 items are rendered with an onClick that is re created every time App re renders. This will not only cause the Items to re render it will also cause virtual DOM compare to fail and React will re create the Itms in the DOM (expensive operation).

The last 2 items get an onClick that is created when App mounts and not re created when App re renders so they will never re render.

const { useState, useCallback, useRef, memo } = React;
constItem = memo(functionItem({ onClick, id }) {
  const rendered = useRef(0);
  rendered.current++;
  return (
    <button_id={id}onClick={onClick}>
      {id} : rendered {rendered.current} times
    </button>
  );
});
constApp = () => {
  const [message, setMessage] = useState('');
  constonClick = (e) =>
    setMessage(
      'last clicked' + e.target.getAttribute('_id')
    );
  const memOnClick = useCallback(onClick, []);

  return (
    <div><h3>{message}</h3>
      {[1, 2].map((id) => (
        <Itemkey={id}id={id}onClick={onClick} />
      ))}
      {[1, 2].map((id) => (
        <Itemkey={id}id={id}onClick={memOnClick} />
      ))}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script><divid="root"></div>

Another example is when you want to call a function in an effect that also needs to be called outside of the effect so you can't put the function inside the effect. You only want to run the effect when a certain value changes so you can do something like this.

//fetchById is (re) created when ID changesconst fetchById = useCallback(
  () =>console.log('id is', ID),
  [ID]
);
//effect is run when fetchById changes so basically//  when ID changesuseEffect(() =>fetchById(), [fetchById]);

Your second question:

The setValues({ ...prev, [name]: value }) will give you an error because you never defined pref but if you meant: setValues({ ...values, [name]: value }) and wrap the handler in a useCallback then now your callback has a dependency on values and will be needlessly be re created whenever values change.

If you don't provide the dependency then the linter will warn you and you end up with a stale closure. Here is an example of the stale closure as counter.count will never go up because you never re create onClick after the first render thus the counter closure will always be {count:1}.

const { useState, useCallback, useRef } = React;
constApp = () => {
  const [counts, setCounts] = useState({ count: 1 });
  const rendered = useRef(0);
  rendered.current++;
  const onClick = useCallback(
    //this function is never re created so counts.count is always 1//  every time it'll do setCount(1+1) so after the first//  click this "stops working"() =>setCounts({ count: counts.count + 1 }),
    [] //linter warns of missing dependency count
  );
  return (
    <buttononClick={onClick}>
      count: {counts.count} rendered:{rendered.current}
    </button>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script><divid="root"></div>

Post a Comment for "Usage Of Usecallback And Setting New Object State Using Previous State As Argument"