skip to Main Content

I have an array in redux. I am storing its value through useSelector in variable named masterStatusOptions.
Now masterStatusOptions is also one of the dependency array of my useEffect.
The logic that I have written in that use effect is updating the state variable which is correct.
When that use Effect is executed 1 time it is re rendering my component and now useSelector returns me the same array but I think the memory location changed. That is why again useEffect is called and since it is rerendering because state is changed my program goes into an infinite loop. How can I solve this?

const masterStatusOptions = lookupTypeData(useSelector((state) => state.lookup.lookup), 'user_status'); // This returns an array of lookup values
  const [statusOptions, setStatusOptions] = useState();
  useEffect(()=>{
    if(masterStatusOptions?.length){
      let masterArr = masterStatusOptions.filter((item) => item.priority !== 999);
      console.log(masterArr);
      setStatusOptions(masterArr); // Due to this rerender is triggered and masterStatusOptions value becomes a new memory address since it is returning an array and the whole code goes into infinite loop.
    }
  },[masterStatusOptions])

2

Answers


  1. statusOptions shouldn’t be it’s own state, since it can be directly calculated from other values. As a happy side effect, this will fix your bug because it deletes the useEffect entirely:

    const masterStatusOptions = lookupTypeData(useSelector((state) => state.lookup.lookup), 'user_status');
    let statusOptions;
    if (masterStatusOptions?.length) {
      statusOptions = masterStatusOptions.filter((item) => item.priority !== 999)
    }
    

    This approach is simpler to write, doesn’t cause a performance penalty from double renders, and statusOptions and masterStatusOptions will never be out of sync, even briefly.

    If filtering the array is a performance concern, you can put it in a useMemo so you don’t repeat the calculation until the array changes:

    const masterStatusOptions = lookupTypeData(useSelector((state) => state.lookup.lookup), 'user_status');
    let statusOptions = useMemo(() => {
      if (masterStatusOptions?.length) {
        return masterStatusOptions.filter((item) => item.priority !== 999)
      } else {
        return undefined
      }
    }, [masterStatusOptions]);
    
    Login or Signup to reply.
  2. The code you provided seems to be creating an infinite loop of re-renders because of the way you are using the masterStatusOptions variable from your Redux store. When you use useSelector to access data from the Redux store, it creates a new reference to the selected data every time the store updates. This means that the masterStatusOptions variable in your component will have a new reference whenever the Redux store is updated, causing your useEffect to run again and again in an infinite loop.

    To fix this issue and prevent the infinite loop, try using the useMemo hook to memoize the masterStatusOptions variable and ensure that it only changes when the actual data inside it changes:

    const masterStatusOptions = useSelector((state) => state.lookup.lookup); // assuming the default value is []
    
    const statusOptions = useMemo(() => {
      return masterStatusOptions.filter((item) => item.priority !== 999)
    }, [masterStatusOptions]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search