0

Wrote some code to compile results from different projects and update useState hook, doesn't work as expected.

PS - adding dependency in useEffect's argument to redo the function twice to update itself isn't helping either.

Briefing for easy understanding of whats going on in code:

  1. I have a multiple-selection input field.
  2. Based on input values, a useState 'selectedProjectScopeData' gets updated after running an api call through another function.
  3. I calculate the total sum from the above mentioned state and store it in another state 'summedSelectedProjectScopeData' to display in react
  4. Issue - The result I get is always one stage lagging behind, the state is not working with updated values.
const classes = useStyles();
    const { approvedProjects } = useSelector(state => state.finance)
    const { userDetails } = useSelector(state => state.user)
    const buttonRef = useRef(null)
    const [selectedProjectsScopedData, setSelectedProjectsScopedData] = useState([]);
    const [summedSelectedProjectsScopedData, setSummedSelectedProjectsScopedData] = useState({Carpentry: {areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Procurement_Others: {areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Modular:{areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Services:{areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Hardware:{areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}})
    const [scopeModal, setScopeModal] = useState(false)
    const [scopedProducts, setScopedProducts] = useState([])
    const [scope, setScope] = useState("")
    const [projectInfo, setProjectInfo] = useState([])
    const [toggle, setToggle] = useState(false)


    useEffect(() => {
        getApprovedProjects()
        // eslint-disable-next-line
    }, [])

    // useEffect(()=>{
    //     ProjectDetailsAccumulator();
    //     console.log('selectedProjectsScopedData is ',selectedProjectsScopedData)
    //     console.log('summedSelectedProjectsScopedData is ',summedSelectedProjectsScopedData)

    // },[selectedProjectsScopedData])

    // useEffect(() => {ProjectDetailsAccumulator()},[selectedProjectsScopedData])

    


    // runs the http request and adds data in the required array
    const getProjectsScopedData = async (projectId, newArr) =>{
        // console.log("Function 2: Step 1 : getProjectsScopedData runs")
        try{                                //If data is not found we call make an httpcall for that data and add it to the array
            const response = await HttpService.getProjectsScopedData(projectId)
            const newData = await response.data;
            // console.log("Function 2: Step 2 : http request runs and gives newData as ",newData);
            newArr.push(newData) //adding data to array of selected projects
        }
        catch(error){
            console.log(error)
            props.setSnackInfo(error?.response?.data?.message ?? "Error while fetching project scope data", "error")
        }
    }

    //runs when the projects in multiple selection changes
    const changeHandler = async (event,values) => {
        // console.log("Function 1: Step 1 : change handler runs")
        try {
        props.setLoading(true)
        let newArr = []  // new array to store the new project data called from ==> const response = await HttpService.getProjectsScopedData(projectId)

        // console.log("Function 1: Step 2: values recieved are : ", values)
        values.forEach((selectedValue) => {                      // values contain project details and projectId
            const foundProject = selectedProjectsScopedData.find((projectElement) => {
              return projectElement.projectId === selectedValue._id;
            });
            if(foundProject) {
                newArr.push(foundProject)
            }
            else{
                getProjectsScopedData(selectedValue._id,newArr)
            }
        })
        // console.log("Function 1: Step 3: newArr to be fed into state is", newArr)
        setSelectedProjectsScopedData(newArr);             // we assign the data to the selectedProjectsScopedData state
        ProjectDetailsAccumulator()
        props.setLoading(false)
    }
        catch(error){
            console.log(error);
            props.setLoading(false)
            props.setSnackInfo("Couldn't fetch Projects Scoped Data", "error")
        }
    }


    //runs at the end of changeHandler to sum up details from all the projects that are selected
    const ProjectDetailsAccumulator = ()=>{
        try{
        let ProjectDetails = {Carpentry: {areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Procurement_Others: {areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Modular:{areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Services:{areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}, Hardware:{areaOrQty:0, materialRequested: 0, tdp:0, poRequested:0, soRequested:0}}
        
        //iterating through all the projects in the selectedProjectsScopedData
        // console.log('Function 3: STEP 1: selectedProjectsScopedData is: ', selectedProjectsScopedData)
        // console.log('Function 3: STEP 1: selectedProjectsScopedData[0] is: ', selectedProjectsScopedData[0])
        // console.log('Function 3: STEP 1-1: selectedProjectsScopedData.length is is: ', selectedProjectsScopedData.length)
        selectedProjectsScopedData.forEach((iproject)=>{
            console.log('inside forEach loop')
            for(const itype of iproject.result){  //Accessing object for each type from result array of types
                const currenttype = itype?.type
                // console.log('+itype?.totalDecorpotPrice is', +itype?.totalDecorpotPrice)
                ProjectDetails[currenttype].areaOrQty += itype?.area ? (+itype?.area || 0) : (+itype?.quantity || 0);
                ProjectDetails[currenttype].tdp += +itype?.tdp || 0;
                ProjectDetails[currenttype].poRequested += +itype?.poRequested || 0
                ProjectDetails[currenttype].soRequested += +itype?.soRequested || 0
                ProjectDetails[currenttype].materialRequested += +itype?.materialRequested || 0
            }
        })
        // console.log('Function 3 : STEP 2: project details are', ProjectDetails);
        setSummedSelectedProjectsScopedData(ProjectDetails);
    }
     
    catch(error){
        console.log(error.message)
            }
    }

I tried putting both states in useEffects argument with the function that updates it, but it threw the code into an obvious infinte loop in that case.

  • Which specific operation in all of this code isn't doing what you expect? Can you reduce this to a [mcve] which demonstrates the problem more clearly? – David Aug 03 '23 at 13:56
  • 1
    There are some other issues with the code => `getProjectsScopedData` is an async call, you will have to `await` it or use `.then()`. Also when working with async operations, try to avoid using `forEach`, use a regular `for of` loop instead. – axtcknj Aug 03 '23 at 14:01
  • Hey David, the function 'ProjectDetailsAccumulator' isn't working as expected, the state summedSelectedProjectsScopedData should provide summed data that comes from running a loop on another state 'selectedProjectsScopedData'; – Surviving_JS Aug 04 '23 at 05:24
  • Hey axtcknj, can you please be more clear as i am already using async and await keywords in the 'getProjectsScopedData' function, awaiting the http response, then using await while reading value. – Surviving_JS Aug 04 '23 at 05:27
  • I forgot to update but this code worked, instead of using different functions, I wrote the code in one function. Got the data, updated state by destructuring old state and adding a new entry I guess the issue was that I was passing around a simple array(let) and then adding entries in it expecting it to work, although it worked after condensing the code, Im still not sure what exactly was wrong with my earlier code. – Surviving_JS Aug 09 '23 at 13:04

0 Answers0