Search code examples
javascriptreactjsreact-hooksuse-effectuse-state

waiting for useeffect to finish and then accessing object properties


I have the code below:

const [files, setFiles] = useState([]);
const [greeting, setGreeting] = useState(0);
const [menu, setMenu] = useState([]);
const [options, setOptions] = useState([]);
var customer_id = 1;

useEffect(() => {
    async function getFiles() {
      
      var data = {
        "customer-id": customer_id
      };
      let response = await axios.post(`http://localhost:2000/files/get-files`, data)
      //let response = await axios.post(`/files/get-files`, data)
      for (let i = 0; i < response.data.length; i++) {
        setOptions(oldArray => [...oldArray, {'value': response.data[i]['id'], 'label': response.data[i]['name']}])
      }
      setFiles(response.data)
    }
    async function getIVR() {
      
        var data = {
          "customer-id": customer_id
        };
        let response = await axios.post(`http://localhost:2000/files/get-ivr`, data)
        //let response = await axios.post(`/files/get-ivr`, data)
        
        setGreeting(response.data[0]['greeting'])
        setMenu(response.data[0]['menu'])
    }

    getFiles()
    getIVR() 
}, []);
var test = (options.find(x => x.value === greeting))
console.log(test.get('label'))

The code above returns an error:

TypeError: Cannot read property 'get' of undefined

This is because on first render label does not exist as options is not populated yet. However, when I just console.log(test), I can see that after a few ms it is populated as shown by the logs:

27GreetingMenu.js:53 undefined
GreetingMenu.js:53 undefined
GreetingMenu.js:53 {value: 3, label: "Test Menu"}
GreetingMenu.js:53 {value: 3, label: "Test Menu"}
GreetingMenu.js:53 {value: 3, label: "Test Menu"}
GreetingMenu.js:53 {value: 3, label: "Test Menu"}

How can I make it so that I can always get the value of label. This can be accomplished by somehow waiting for useeffect to finish which means options is good which means I can parse it.

Let me know!


Solution

  • I think you're trying to solve the wrong problem here.

    First: This is because on first render label does not exist as options is not populated yet - isn't completely true. The problem isn't that the label property isn't set, its that test is undefined since find did not get a match.

    Instead of trying to wait for a useEffect to complete (you can't directly), you should just do a simple check to make sure test is defined before using it as an object.

    var test = (options.find(x => x.value === greeting))
    
    // Easiest solution
    console.log(test?.get('label'))
    
    // Without nullsafe operator
    console.log(test && test.get('label'))
    

    As a side note, since you may run into this error next: I don't see a get method being added to the objects inside options? So you may get an error next saying .get is not a function.

    It looks like maybe you are assuming test will be an Immutable object? But I don't see code anywhere making that the case.