-1

I've been trying to deconstruct an object. The object comes via json as a result of single column select from the database.

//api
export const fetchNames = async() => {
  try {
    const data = await axios.get("http://localhost:5000/names");  
    return data;
  } catch (error) {
    return error
  }
}

//function call
const [fetchedNames, setfetchedNames] = useState([]);

useEffect(()=>{
  const fetchApi = async () => {
    fetchedNames(await fetchNames());     
  }
  fetchApi();
  console.log(fetchedNames);
})

result: data:Array(23)

0:{name:"adams"}

resultscreenhost

Expected is an array of all names. [ADAMS, SIMON, ...]. The array will be use in a NativeSelect and will be display as frontend selection.


Approach i did that resulted to my expected output.

  export const fetchNames = async () =>{
       try{
  
   const response = await fetch(`http://localhost:5000/towns`);
    const jsonNames = await response.json();
    return jsonNames;

   }catch(error){
      return error;
    } 
  }


 const [fetchedNames, setFetchedNames] = useState([]);
  useEffect(()=>{
    const fetchApi = async () =>{
        setFetchedNames( await fetchNames());
    }
    fetchApi();
  },[]);

Then, i did the mapping. {fetchedNames.map((Names,i) => (<option key={i} value {Names.name}>{Names.name}))}

  • [There's no such thing as a "JSON Object"](http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/) – Andreas Aug 27 '21 at 10:18
  • 5
    `fetchedNames(await fetchNames());` `fetchedNames` is an *array*, not a function. Also note that logging `fetchedNames` after using its state setter will still show its old value; the new value shows up when your component function gets called again to handle the state change. – T.J. Crowder Aug 27 '21 at 10:18
  • 3
    Not related to the problem but you probably don't want to omit the dependency array of the `useEffect` hook because without the dependency array, `useEffect` will execute after each re-render. – Yousaf Aug 27 '21 at 10:23

3 Answers3

4

There are a few issues there:

  • fetchedNames(await fetchNames()); is trying to call an array, not a function; the setter is setFetchedNames, not fetchedNames.
  • You're converting rejection to fulfillment with an error.
  • You don't have a dependency array, so the effect gets called after every render.
  • There's no proper error handling when using the API function.
  • Doing console.log(fetchedNames) immediately after calling its setter will still show you the old value; your component sees the new value when React calls it again later to re-render because of the state change.

I think you're probably looking for something like this, assuming you only want to fetch the data once when the component mounts, see *** comments:

//api
export const fetchNames = async () => {
    // *** Removed the `try`/`catch`, you shouldn't handle that here, let the caller handle it
    const data = await axios.get("http://localhost:5000/names");
    return data; // *** Is this really correct? Not `data.data`?
}; // *** I assumed a closing } here

// In your component function
const [fetchedNames, setfetchedNames] = useState([]);

useEffect(() => {
    // An async function wrapper doesn't help anything here
    fetchNames()
    .then(setFetchedNames) // *** Call the setter function, not the array
    .catch(error => {
        // ...handle/report error...
    });
}, []); // *** You need a dependencies array, or your effect is called after every render

// ...use `fetchedNames` to render the component; it will initially be
// empty, then your component will be re-rendered (with the names in
// `fetchedNames`) when you get the names from the API.

Aside from the above, if you only want the names but the array you get is of objects with a name property, add a map call, probably in the API function:

export const fetchNames = async () => {
    const data = await axios.get("http://localhost:5000/names");
    return data.map(({name}) => name);
    //         ^^^^^^^^^^^^^^^^^^^^^^
};
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Thank you for your patience in going through my bad code. I appreciate it. I'm obviously still need to learn a lot. You comments above made me realize my mistakes.Though i did a different approach and got my expected result. – newincodin Aug 27 '21 at 14:54
  • @newincodin - Glad if that helped! :-) Happy coding! – T.J. Crowder Aug 27 '21 at 15:09
1

As you mentioned, if your API response shape is like data array, you can simply use map function to get all the values to an array.

const data = [{name: 'A'}, {name: 'B'}, {name: 'C'}, {name: 'D'}];
const dataArray = data.map(entry => entry.name);
console.log(dataArray); // ["A","B","C","D"]
0
export const fetchNames = async() =>{
  try {
    const data = await axios.get("http://localhost:5000/names");  
    return data;
        
} catch (error) {
    return error
}


//function call[enter image description here][1]
const [fetchedNames, setfetchedNames ]= useState([]);

 useEffect(()=>{
  fetchNames().then((data) => {
setfetchedNames(p => [...p, data.names])
})
 console.log(fetchedNames);
})

you can try to type this code instead it uses the returned value from fetchNames then stores each name in the state fetchedNames.

Hakim art
  • 9
  • 2