imnewhere
imnewhere

Reputation: 53

Adding JSON data to React

I have been able to pull data from an API that I built using MongoDB and Express, but am having trouble rendering the nested data to my React component.

For example, if I type in <p>{restaurant.cuisine}</p> I am able to retrieve Burgers, American, but if I try and access {restaurant.status.delivery}, I get an error that says:

Cannot read property 'delivery' of undefined.

But if I {console.log(restaurant.status} I can see the object? I tried turning the object into an array using Object.values, but that didn't work either.

enter image description here

The same thing happens if I try to access the nested objects in {restaurant.images} and {restaurant.geometry}.

Here's a copy of my React hook:

import { useReducer, useEffect } from 'react';
import axios from 'axios';

const ACTIONS = {
  MAKE_REQUEST: 'make-request',
  GET_DATA: 'get-data',
  ERROR: 'error',
};

function reducer(state, action) {
  switch (action.type) {
    case ACTIONS.MAKE_REQUEST:
      return { loading: true, restaurant: [] };
    case ACTIONS.GET_DATA:
      return {
        ...state,
        loading: false,
        restaurant: action.payload.restaurant,
      };
    case ACTIONS.ERROR:
      return {
        ...state,
        loading: false,
        error: action.payload.error,
        restaurant: [],
      };
    default:
      return state;
  }
}

export default function useFetchSingleRestaurant({ id }) {
  const [state, dispatch] = useReducer(reducer, {
    restaurant: [],
    loading: true,
  });

  useEffect(() => {
    dispatch({ type: ACTIONS.MAKE_REQUEST });
    axios
      .get('http://localhost:4444/restaurants/' + id)
      .then((res) => {
        dispatch({
          type: ACTIONS.GET_DATA,
          payload: { restaurant: res.data.restaurant },
        });
      })
      .catch((e) => {
        dispatch({
          type: ACTIONS.ERROR,
          payload: { error: e },
        });
      });
  }, [id]);

  return state;
}

I'm accessing it in my SingleRestaurant component:

function SingleRestaurant({ match }) {
  const { restaurant } = useFetchSingleRestaurant({ id: match.params.id });

  return ( 
    <p>{restaurant.status.delivery}</p>
  ) 
}

And then here's my backend setup as well:

showRestaurant = async (req, res) => {
  const restaurant = await Restaurant.findById(req.params.id)
    .populate({ path: 'reviews', populate: { path: 'author' } })
    .populate('author');
  if (!restaurant) {
    req.flash('error', 'Restaurant not found.');
    return res.redirect('/restaurants');
  }
  res.send({ restaurant });
};

Upvotes: 1

Views: 61

Answers (1)

Thomas Collins
Thomas Collins

Reputation: 317

Until your server request returns restaurant it will be set as the default [] that you have set.

An empty array does not have a property of status, so hence the error.

if you change your default to null:

const [state, dispatch] = useReducer(reducer, {
    restaurant: null,
    loading: true,
  });

And then check for a value:

function SingleRestaurant({ match }) {
  const { restaurant } = useFetchSingleRestaurant({ id: match.params.id });
  if (!restaurant) return 'Loading'

  return ( 
    <p>{restaurant.status.delivery}</p>
  ) 
}

You could also pass back the loading state from your hook and then do a check on that.

Upvotes: 2

Related Questions