0

I have a page wherein there are Listings. A user can check items from this list.

Whenever the user checks something it gets added to a globally declared Set(each item's unique ID is added into this set). The ID's in this set need to be accessed by a seperate Component(lets call it PROCESS_COMPONENT) which processes the particular Listings whose ID's are present in the set.

My Listings code roughly looks like:

import React from "react";
import { CheckBox, PROCESS_COMPONENT } from "./Process.jsx";

const ListItem = ({lItem}) => {
    return (
            <>
               //name,image,info,etc.
               <CheckBox lId={lItem.id}/>
            </>
)
};

function Listings() {
 // some declarations blah blah..
 return (
       <>
          <PROCESS_COMPONENT /> // Its a sticky window that shows up on top of the Listings.
          //..some divs and headings
          dataArray.map(item => { return <ListItem lItem={item} /> }) // Generates the list also containing the checkboxes
       </>
)
}

And the Checkbox and the PROCESS_COMPONENT functionality is present in a seperate file(Process.jsx).

It looks roughly like:

import React, { useEffect, useState } from "react";

let ProcessSet = new Set(); // The globally declared set.

const CheckBox = ({lID}) => {
   const [isTicked, setTicked] = useState(false);
   const onTick = () => setTicked(!isTicked);
   useEffect( () => {
     if(isTicked) {
          ProcessSet.add(lID);
     }
     else { 
          ProcessSet.delete(lID); 
     }
     console.log(ProcessSet); // Checking for changes in set.
   }, [isTicked]);
   return (
      <div onClick={onTick}>
       //some content
      </div>
   )
}

const PROCESS_COMPONENT = () => {
   const [len, setLen] = useState(ProcessSet.size);
   useEffect( () => {
      setLen(ProcessSet.size);
   }, [ProcessSet]); // This change is never being picked up.
   return (
        <div>
        <h6> {len} items checked </h6>
        </div>
   )
}

export { CheckBox, PROCESS_COMPONENT };

The Set itself does get the correct ID values from the Checkbox. But the PROCESS_COMPONENT does not seem to be picking up the changes in the Set and len shows 0(initial size of the set).

I am pretty new to react. However any help is appreciated.

Edit:

Based on @jdkramhoft 's answer I made the set into a state variable in Listings function.

const ListItem = ({lItem,set,setPSet}) => {
   //...
        <CheckBox lID={lItem.id} pset={set} setPSet={setPSet} />
   )
}

function Listings() {
  const [processSet, setPSet] = useState(new Set());
  //....
      <PROCESS_COMPONENT set={processSet} />
      dataArray.map(item => { 
          return <ListItem lItem={item} set={processSet} setPSet={setPSet} /> 
      })
}

And corresponding changes in Process.jsx

const CheckBox = ({lID,pset,setPSet}) => {
  //...
  if (isTicked) {
     setPSet(pset.add(lID)); 
  }
  else {
     setPSet(pset.delete(lID));
  }
  //...
}

const PROCESS_COMPONENT = ({set}) => {
   //...
   setLen(set.size);
   //...
}

Now whenever I click the check box I get an error:

TypeError: pset.add is not a function. (In 'pset.add(lID)', 'pset.add' is undefined)

Similar error occurs for the delete function as well.

3
  • 1
    If you want changes to the Set to be reactive simply store it as a state variable at the appropriate parent level, or within a context which can be subscribed to by those components that need it. Commented Nov 10, 2021 at 8:41
  • @pilchard Okay. But where exaclty do I move the set to? Even in the Listings file there are two function components that need access to it. And declaring it globally in Listings and passing it as a prop to both CheckBox and PROCESS_COMPONENT also seems to yield the exact same problem. Commented Nov 10, 2021 at 8:56
  • @pilchard nvm. Found the appropriate parent. It is the Listings function. Silly ol me. Thanks for the help! Commented Nov 10, 2021 at 9:09

1 Answer 1

1

First of all, the set should be a react state const [mySet, setMySet] = useState(new Set()); if you want react to properly re-render with detected changes. If you need the set to be available to multiple components you can pass it to them with props or use a context.

Secondly, React checks if dependencies like [ProcessSet] has been changed with something like ===. Even though the items in the set are different, no change is detected because the object is the same and there is no re-render.

Update:

The setState portion of [state, setState] = useState([]); is not intended to mutate the previous state - only to provide the next state. So to update your set you would do something like:

  const [set, setSet] = useState(new Set())
  const itemToAdd = ' ', itemToRemove = ' ';
  setSet(prev => new Set([...prev, itemToAdd]));
  setSet(prev => new Set([...prev].filter(item => item !== itemToRemove)));

As you might notice, this makes adding and removing from a set as slow as a list. So unless you need to make a lot of checks with set.has() I'd recommend using a list:

  const [items, setItems] = useState([])
  const itemToAdd = ' ', itemToRemove = ' ';
  setItems(prev => [...prev, itemToAdd]);
  setItems(prev => prev.filter(item => item !== itemToRemove));
Sign up to request clarification or add additional context in comments.

6 Comments

Okay. So I did this. Created a useState variable in the Listings function. Passed the variable and the setter as props to CheckBox and PROCESS_COMPONENT. But now the Set.add and Set.delete within the CheckBox component seems to fail and throw an undefined error.
Updated this in the question.
Yes, you can’t simply add anymore as this will mutate state. You’ll need to pass a callback to setState clone the set and then add.
@pilchard How exactly am I supposed to do it? I tried cloning the set within useEffect of CheckBox by doing a let pset_clone = new Set(pset); and then pass that into setPSet() but still no avail. It just drops a TypeError: Type Error.
I’m on mobile at the moment, but can type it up later.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.