1

I am trying to fetch some data using axios. I have this state of items:

const [subGroup, setSubGroup] = useState([])
const [items, setItems] = useContext(ItemsContext)

I am then using this items state, to filter some items, store them in another array state and then render them.

const getSubGroups = () => {
    console.log(items) // still empty array
    
        const [group] = items.filter(item => (
            item.name === match.params.group ? item.subgroups : null
        ))
        const subgroups = group.subgroups

        setSubGroup(subgroups)
    

}

useEffects(I only had the second one, but now I want to update the items' state on refresh, so this is the reason why there are two):

useEffect(async () => {
    await axios.get('http://localhost:3001/Items')
        .then(res => setItems(res.data));
    console.log(items)
    getSubGroups()
    }, [])
useEffect(() => {
    getSubGroups()
},[match.params.group])

The console log return an empty array and I get an error of 'Cannot read property 'subgroups' of undefined. I tried using the method without async and await keywords. Same result. How do I ensure useEffect gets the data and then to proceed with rendering items? Any help would be appreciated. Note: res.data returns the array that I need properly.

EDIT:

What about if I want to access even more nested data? I looked at your example, it happens to work in the case, but what if the case is this? I tried following the logic but I got no output:

const [items, setItems] = useContext(ItemsContext)
//each item has nested array data
const data = useMemo(() => {
    const [subWithData] = items.filter(item => (
        item.name === match.params.group ? item.subgroups : null
    )).filter(item => (
        item.subgroups.map(sub => (
            sub.name === match.params.subGroup
        ))
    ))
    return subWithData?.data?? []


}, [match.params.group, match.params.subGroup, items])

Where am I doing a mistake here? Sorry if the question is too lame but I am new.

1 Answers1

0

Instead of moving your heavy (though, it is not heavy in your current case) computation in return statement, you can use useMemo hook to memoize the results and not re-compute if the source / dependency hasn't changed:

const [items, setItems] = useContext(ItemsContext)

const subGroups = useMemo(() => {
  const [group] = items.filter(item => (
    item.name === match.params.group
  ))
  return group?.subgroups ?? []
}, [match.params.group, items])

useEffect(() => {
  axios.get('http://localhost:3001/Items')
    .then((res) => setItems(res.data))
}, [])

return <>
  <div>{  ... you can use subGroups here  ... }</div>
</>
Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87
  • @rollstapewz I saw your edit. Why are you chaining your `filter` i.e. using it 2 times? You can do all the filtering in 1 filter. Technically it (what you wrote in edited part) should work. If it is not working, its reason might be the *data* and *filter* i.e. you applied wrong filters and hence it is returning `[]`. So, the best way to fix your issue would be that you create a sandbox to reproduce this issue. – Ajeet Shah Feb 16 '21 at 17:45