0

Im trying to set a counter everytime this condition is met in every array.push :

    interface FilterProps {
            filterQuery: any
            setFilterQuery: (e: any) => any
            resetFilter: (e: any) => any
            handleCategory: (e: any) => any
            categoryList: any
            getCategoryValue: any
            handleOnClick: (e: any) => any
            paginateOnClick: (e: any) => any
            paginateIcon: any
            handleToggle: (e: any) => any
            checkState: any
            expandFilter: boolean
            printEvent: PrintEvent
        }
        
        export const EventFilter: React.FC<FilterProps> = ({
            filterQuery,
            setFilterQuery,
            resetFilter,
            handleCategory,
            categoryList,
            getCategoryValue,
            handleOnClick,
            paginateOnClick,
            paginateIcon,
            handleToggle,
            checkState,
            expandFilter,
        }, printEvent: PrintEvent) => {
        
        
             const [countUnlabeled, setCountUnlabeled] = React.useState(0)
            
                const classes = useStyles()
            
                const { box, ui } = useStores()
            
                const { labels } = printEvent
            
                let unlabeledEvents: any[] = []
            
                function getUnlabeled() {
                    box.descEvents.forEach((printEvent: PrintEvent) => {
            
            
         
            
                        const isStopEvent =
                            (printEvent && printEvent.name === 'control_fault') ||
                            (printEvent.name === 'running' && printEvent.value === false) ||
                            (printEvent.name === 'safety_chain' && printEvent.value === false) ||
                            (printEvent.name === 'torch_collision' && printEvent.value === true) ||
                            (printEvent.name === 'motion_sup' && printEvent.value === true) ||
                            (printEvent.name === 'e_stop' && printEvent.value === true)
            
                        const unlabeled = printEvent.labels === null && isStopEvent
            
            
                        if (unlabeled) {
                            unlabeledEvents.push(unlabeled)
                            ui.setUnlabeledCount(unlabeledEvents.length)
                        }
            
                    })
                }
    
    
        useEffect(() => {
            if (box.descEvents && printEvent) {
                getUnlabeled()
                console.log('useEffect just ran', ui.unlabeledCount, unlabeledEvents.length)
            }
        }, [unlabeledEvents, ui.unlabeledCount, printEvent.name])
    
        return (
            <Accordion
                className={classes.eventAccordion}
                TransitionProps={{ unmountOnExit: true }}
                defaultExpanded={expandFilter}
            >
                <AccordionSummary>
                    <div className={classes.filterHeader}>
                        <div className={classes.filterText}>
                            <FilterListIcon />
                            <p>Filter by:</p>
                        </div>
                        <div className={classes.unfiltered}>
                            Unlabeled events: &nbsp;
                            <Chip
                                size="small"
                                label={ui.unlabeledCount}
                                className={classes.chipMissing}
                            />
    
                        </div>
    
                    </div>
                </AccordionSummary>
    </Accordion>

export default EventFilter

normally it should run the functuion check everytime the event is pushed or there are changes in the array, but its not counting sychronously. i tried adding a count to the unlabeled conditional but doesnt work and dont want to overcomplicate things here.

What is the problem here?

counter example

  • 1
    Effects are run after render. Setting state causes renders. You have a setState call (or what looks like one) inside a loop. Setting state is async and batched. What are you really trying to do? – Nikki9696 Jan 25 '23 at 18:44
  • basically to increment the counter (unlabeled events) everytime the unlabeled condition is met. – Farid Guzman Jan 25 '23 at 19:05

1 Answers1

0

In React, stuff you write inside the functional component body is run on every render. That means

let unlabeledEvents: any[] = [];
useEffect(() => {
    console.log(unlabeledEvents);
}, [unlabeledEvents]);

creates a fresh array every time the component is rendered, leaving the object reference (or length, or any other property) unchanged. If you want to react changes to an object, you need a way to store the old reference somewhere and then update it in a way that creates a new reference. This is exactly what the useState hook is for. Just make sure a new array is created every time in order to update that reference:

const [unlabeledEvents, setUnlabeledEvents] = useState<any[]>([]);

function getUnlabeled() {
     // find unlabeled item
     if (unlabeled) {
          setUnlabeledEvents([...unlabeledEvents, unlabeled]);
     }
}

This way your useEffect runs every time getUnlabeled adds a new entry to the unlabeledEvents array. You can also ditch countUnlabeled, since unlabeledEvents will always be up to date, you can use its length directly instead.

However, are you sure your useStores hooks works as expected? Since we don't know anything about it, it could suffer from the same problem as described above. And if it does, I'd recommend using useMemo instead, to recalculate the array every time box is changed (since you're iterating the entire array anyways):

const { box } = useStores();

const unlabeledEvents = useMemo(
    box.descEvents.filter((e) => true /* your unlabeled condition here */),
    [box]
)

Also check out this question for some more details on updating stateful arrays in a component.

Szigeti Péter
  • 160
  • 1
  • 4