skip to Main Content

In React I have RecipeListPage.jsx with recipes. Clicking on a recipe should show RecipePage.jsx with only the clicked recipe info. However, my onClick code gives the error ‘obj’ is not defined.eslint no-undef

        <Flex wrap='wrap' gap={8} justify='center' my={2}>
          {list.map((list) => (
            <Card
              bgColor='white'
              overflow='hidden'
              borderRadius={15}
              h='md'
              w='xsm'
              cursor='pointer'
              _hover={{ transform: 'scale(1.03)' }}
              key={list}
              onClick={() => clickFn(obj.recipe)}
            >

I’ve been looking if I need to change something in eslint, but seems to be fine:

{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:react/jsx-runtime"
    ],
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": ["react"],
    "rules": {
        "react/prop-types": 0
    }
}

In my VS Code this error shows a link to the following page:
https://eslint.org/docs/latest/rules/no-undef

Does anyone know how to solve this please?

Update:
I tried adding "no-undef" to eslintrc.json, that does get rid of the error. However, the code still doesn’t work.

"rules": {
"no-undef": "off"
}

This just turns the warning of, but the obj still isn’t defined according to browser console:
"Uncaught ReferenceError: obj is not defined"
This error refers to onClick={clickFn(obj.recipe)}

Just to be sure, this is the entire RecipeListPage.jsx:

import { data } from '../utils/data';
import { TextInput } from '../components/ui/TextInput';
import { useState } from 'react'
import {
    Flex,
    Card,
    CardBody,
    Center,
    Text,
    Image,
    Stack,
    Tag,
    Heading,
} from '@chakra-ui/react';

export const RecipeListPage = ({ clickFn }) => {
  const [searchField, setSearchField] = useState('');
  
  const handleChange = (event) => {
    setSearchField(event.target.value);
  };

  const matchedRecipe = data.hits.filter(obj =>
    obj.recipe.label.toLowerCase().includes(searchField.toLowerCase()));

  let displayMatchedRecipe = data.hits;
  if (matchedRecipe.length > 0) {
    displayMatchedRecipe = matchedRecipe;
  }

  const list = displayMatchedRecipe.map(obj =>
    <>
      <Image key={obj.recipe.image}
        src={obj.recipe.image}
        h='10rem'
        w='100%'        
        mt={-1}        
        borderTopLeftRadius={15}        
        borderTopRightRadius={15}        
        objectFit='cover'  
      />
      <Text textTransform='uppercase' key={obj.recipe.mealType}>
        {obj.recipe.mealType}
      </Text>
      <Text
        fontSize={18}
        fontWeight='bold'
        textAlign='center'        
        key={obj.recipe.label}
      >
        {obj.recipe.label}
      </Text>
      <Flex gap={1} key={obj.recipe.healthLabels}>
        {obj.recipe.healthLabels.filter(
          label => label.includes('Vegetarian')).map(filteredLabel => (
            <Tag
              key={filteredLabel}
              textTransform='uppercase'
              fontSize={10}
              bgColor='yellow.400'
              color='gray.900'
            >
              Vegetarian
            </Tag>
          ))}
        {obj.recipe.healthLabels.filter(
          label => label.includes('Vegan')).map(filteredLabel => (
            <Tag
              key={filteredLabel}
              textTransform='uppercase'                                  
              fontSize={10}                                 
              bgColor='yellow.400'                                  
              color='gray.900'                                  
            >
              Vegan
            </Tag>
          ))}
      </Flex>
      <Flex gap={2} justify='center' wrap='wrap' key={obj.recipe.dietLabels}>
        {obj.recipe.dietLabels.map((dietLabels) => (
          <Tag
            key={dietLabels}
            textTransform='uppercase'                                 
            fontSize={10}                                
            bgColor='green.400'                                
            color='gray.900'                                
          >
            {dietLabels}
          </Tag>
        ))}
      </Flex> 
      <Text textTransform='capitalize'
      >
        Dish: {obj.recipe.dishType}
      </Text>
      <Flex justify='center' gap={2} wrap='wrap'>
        <>
          {obj.recipe.cautions.length> 0 && <Text>Cautions: </Text>}
          {obj.recipe.cautions.map((cautions) => (
            <Tag
              key={obj.recipe.cautions}
              textTransform='uppercase'
              fontSize={10}
              bgColor='purple.400'
              color='gray.800'
            >    
              {cautions}
            </Tag>
          ))}
        </>
      </Flex>
    </>
  );

  return (
    <>
      <Center bg='pink.600' flexDir="column" h='100%' >
        <Heading size='xl' mt={2} color='white'>Winc Recipe Checker</Heading>
        <TextInput
          changeFn={handleChange}
          matchedRecipe={matchedRecipe}
          />
        <Flex wrap='wrap' gap={8} justify='center' my={2} >
          {list.map((list) => (
            <Card
              bgColor='white'
              overflow='hidden'
              borderRadius={15}
              h='md'
              w='xsm'
              cursor='pointer'
              _hover={{ transform: 'scale(1.03)' }}
              key={list}
              onClick={clickFn(obj.recipe)}
            >
              <CardBody w='18em' p={0} >
                <Stack direction='column' spacing='2px'>
                  <Image />
                  <Flex flexDirection='column' align='center' gap={1}>
                    {list}
                  </Flex>
                </Stack>
              </CardBody>
            </Card>
          ))}
        </Flex>
      </Center>
    </>
  );
};

2

Answers


  1. So basically what is happening is that you are triying to pass obj.recipe in onClick Function but you have not define the obj variable. I don’t know if it something you are supposed to pass from list variable from map’s prop or is it supposed to a differen variable

    i would also suggest you to bind you function like this: onClick={() => clickFn(obj.recipe)}

    {list.map((list) => (
      <Card
        bgColor='white'
        overflow='hidden'
        borderRadius={15}
        h='md'
        w='xsm'
        cursor='pointer'
        _hover={{ transform: 'scale(1.03)' }}
        key={list}
        onClick={clickFn(obj.recipe)} // Here you are passing `obj`, which is not declared any where in your current scop.
      >
        <CardBody w='18em' p={0} >
          <Stack direction='column' spacing='2px'>
            <Image />
            <Flex flexDirection='column' align='center' gap={1}>
              {list}
            </Flex>
          </Stack>
        </CardBody>
      </Card>
    ))}
    
    Login or Signup to reply.
  2. The error you’re seeing is quite descriptive and it’s basically saying that in the moment that value is being called it doesn’t exist.

    I can see that in your code the obj variable is declared in the inner scopes of the array methods you’re using. In that case you can’t access it’s value on an upper/external scope. You will only be able to access obj value inside the function scope it was declared.

    For example:

    const numbers = [1,2,3]
    
    numbers.map(number => {
      console.log(number) // <-- variable number is only accessible inside this scope
    }
    
    console.log(number) // <-- this will throw an error as number will be undefined
    

    So to solve your issue you should update your code here:

    ...
    onClick={() => clickFn(displayMatchedRecipe[index].recipe)}
    ...
    

    I would also avoid declaring it inline by creating a handler function like this:

    ...
    const handleClick = (recipe) => () => {
      clickFn(recipe);
    };
    
    ...
    
    onClick={handleClick(displayMatchedRecipe[index].recipe)}
    ...
    

    Learn more about javascript scopes in the MDN Page. You can also read more about how to properly call array methods by reading the MDN Page here.

    I also see another issue with your code. You’re using the objects themselves as key and that isn’t recommended as stated in the React Official Documentation.

    Another point is that this, as a component that renders lists can be pretty much optimized and you could define the list component outside this component. I would recommend using the useMemo hook to memoize the lists and avoid extra computation and re-renderings. You can read more about it in the React Official Documentation.

    After all that this is how it could look like:

    import { useMemo } from 'react';
    
    export const RecipeListPage = ({ clickFn }) => {
      const [searchField, setSearchField] = useState('');
    
      const handleChange = (event) => {
        setSearchField(event.target.value);
      };
    
      const matchedRecipe = useMemo(() => data.hits.filter(obj =>
        obj.recipe.label.toLowerCase().includes(searchField.toLowerCase())), [searchField]); 
    
      const list = useMemo(() => matchedRecipe.map(obj => {
        const { recipe } = obj;
        const { image, mealType, label, healthLabels, dietLabels, dishType, cautions } = recipe;
    
        return (
          <>
            <Image key={image} src={image} h='10rem' w='100%' mt={-1} borderTopLeftRadius={15} borderTopRightRadius={15} objectFit='cover' />
            <Text textTransform='uppercase' key={mealType}>{mealType}</Text>
            <Text fontSize={18} fontWeight='bold' textAlign='center' key={label}>{label}</Text>
            <Flex gap={1} key={healthLabels}>
              {healthLabels.filter(label => label.includes('Vegetarian')).map(filteredLabel => (
                <Tag key={filteredLabel} textTransform='uppercase' fontSize={10} bgColor='yellow.400' color='gray.900'>Vegetarian</Tag>
              ))}
              {healthLabels.filter(label => label.includes('Vegan')).map(filteredLabel => (
                <Tag key={filteredLabel} textTransform='uppercase' fontSize={10} bgColor='yellow.400' color='gray.900'>Vegan</Tag>
              ))}
            </Flex>
            <Flex gap={2} justify='center' wrap='wrap' key={dietLabels}>
              {dietLabels.map((dietLabel) => (
                <Tag key={dietLabel} textTransform='uppercase' fontSize={10} bgColor='green.400' color='gray.900'>{dietLabel}</Tag>
              ))}
            </Flex>
            <Text textTransform='capitalize'>Dish: {dishType}</Text>
            <Flex justify='center' gap={2} wrap='wrap'>
              {cautions.length > 0 && <Text>Cautions: </Text>}
              {cautions.map((caution) => (
                <Tag key={caution} textTransform='uppercase' fontSize={10} bgColor='purple.400' color='gray.800'>{caution}</Tag>
              ))}
            </Flex>
          </>
        );
      }), [matchedRecipe]);
    
      const handleClick = (recipe) => () => {
        clickFn(recipe);
      };
    
      return (
        <>
          <Center bg='pink.600' flexDir="column" h='100%' >
            <Heading size='xl' mt={2} color='white'>Winc Recipe Checker</Heading>
            <TextInput changeFn={handleChange} matchedRecipe={matchedRecipe} />
            <Flex wrap='wrap' gap={8} justify='center' my={2} >
              {list.map((item, index) => (
                <Card
                  bgColor='white'
                  overflow='hidden'
                  borderRadius={15}
                  h='md'
                  w='xsm'
                  cursor='pointer'
                  _hover={{ transform: 'scale(1.03)' }}
                  key={index}
                  onClick={handleClick(matchedRecipe[index].recipe)}
                >
                  <CardBody w='18em' p={0} >
                    <Stack direction='column' spacing='2px'>
                      <Image />
                      <Flex flexDirection='column' align='center' gap={1}>
                        {item}
                      </Flex>
                    </Stack>
                  </CardBody>
                </Card>
              ))}
            </Flex>
          </Center>
        </>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search