0

I am very new to React hooks. I have previously used class based components in react and using methods like setState. However this is the first time I am using React Hooks with function based components. So probably not the correct title either. Here is the explanation what I am trying to do. I am trying to create a dataModel which will contain all the data fetched using different apis. Then this model will be passed to different components. This dataModel will be created when the page initially loads. In the below code I am declaring a variable dataModel which will contains keys and each key will have an array or dictionary. I tried few options but it is not working. I have put the code which I tried. I would appreciate the help which would fix it.

import './App.css';
import Query from './components/landing/query';
import React, {useState,useEffect} from 'react';


function App() {  
  const [dataModel,setDataModel] = useState({
    yearOptions: [],
    locOptions: []
  })
  const fetchYear = async()=>{
    await fetch(
      "http://localhost:3002/getYear"
    ).then((response)=>response.json())
    .then((data)=>{
      setDataModel(
           data.map((x)=>{
           let eachx = {key:x.key, text:x.value,value:x.value}
           dataModel.yearOptions.concat(eachx)
  })
      )
    })
  }

  const fetchLoc = async()=>{
    await fetch(
      "http://localhost:3002/getYear"
    ).then((response)=>response.json())
    .then((data)=>{
      setDataModel(
        data.map((x)=>{
          let eachx = {key:x.key, text:x.value,value:x.value}
          dataModel.locOptions.concat(eachx)
      })
      )
    })
  }

  useEffect(() => {
    fetchYear();
    fetchLoc();
  }, []);
 


  return (
    <div className="App">
      <header className="App-header">      
          React Based FrontEnd and Nodejs Based Rest API Demo Project
      </header>

      <Query props={{model:dataModel}}/>
    </div>
    
  );
}

export default App;
Souradip Roy
  • 129
  • 1
  • 1
  • 7
  • 2
    can you recreate this issue in 'codesandbox` or somewhere like that, so we can look into it and try to fix the issue, as it seems to me, `` has the issue, but need to observe more. Can you atleset explain what is your expectation? – Sanira Nimantha Jan 05 '23 at 18:33
  • Hey! Would recommend taking a look at `useReducer`. Definitely a little more complicated if you don't have experience with hooks (or Redux?) but I think it would serve you well in this case – Jack Jan 05 '23 at 18:39
  • The issue is with updating the array of objects state. You can refer to this answer: https://stackoverflow.com/questions/43638938/updating-an-object-with-setstate-in-react – Yogesh Jan 05 '23 at 18:40

2 Answers2

1

you violate the way using of useState. it's not like in class components, either you should use two state variables to store the state, or you should update the state separately and as you may know, react won't rerender if the state does not change, and object literals not show any changes to the state since it's accessed by reference in JS. you can use ES6 spread operator to achieve this. Here's the 2 examples

const [yearOptions,setYearOptions] = useState([])
const [locOptions,setLocOptions] = useState([])

const fetchYear = async()=>{
    await fetch(
      "http://localhost:3002/getYear"
    ).then((response)=>response.json())
    .then((data)=>{
      setYearOptions(
           data.map((x)=>{
           let eachx = {key:x.key, text:x.value,value:x.value}
           dataModel.yearOptions.concat(eachx)
  })
      )
    })
  }

  const fetchLoc = async()=>{
    await fetch(
      "http://localhost:3002/getYear"
    ).then((response)=>response.json())
    .then((data)=>{
      setLocOptions(
        data.map((x)=>{
          let eachx = {key:x.key, text:x.value,value:x.value}
          dataModel.locOptions.concat(eachx)
      })
      )
    })
  }

// then pass the data combined to the component
<Query props={{yearOptions, locOptions}}/>

this is the second method

const [dataModel,setDataModel] = useState({yearOptions: [], locOptions: []})

// if you use your state like above, then you need to do below
// to update the state properly
const fetchYear = async()=>{
    await fetch(
      "http://localhost:3002/getYear"
    ).then((response)=>response.json())
    .then((data)=>{
      const yearData = data.map((x)=>{
           let eachx = {key:x.key, text:x.value,value:x.value}
           dataModel.yearOptions.concat(eachx)
      })
     const tempState = dataModal;
     tempState.yearOptions = yearData;

     // observe this change
     setDataModel({...tempState})
    })
  }

//same thing should do for the locOptions also in fetchLoc function

1st option is the best in my opinion and I guess most react developers would agree with me

Sanira Nimantha
  • 1,160
  • 1
  • 12
  • 29
  • Thank you for helping with the problem. The first method is working. However, the second method is not working as it is passing undefined into the data model. The `const yearData` is showing undefiuned. However `eachx` is correctly showing up the data. – Souradip Roy Jan 06 '23 at 17:08
0

As mentioned in the first answer, the first option works. However for the second option the code is changed by a small fraction. The below is the code for second option of how to set a data model.

const fetchYear=async()=>{
    let tempData = []
    await fetch(
      "http://localhost:3002/getYear"
    ).then((response)=>response.json())
    .then((data)=>{
      data.map((x)=>{
        let eachx = {key:x.key, text:x.value,value:x.value}
        tempData.push(eachx)  
      })
      dataModel.yearOptions = tempData
      setDataModel({...dataModel})
    })
  }
Souradip Roy
  • 129
  • 1
  • 1
  • 7