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
So basically what is happening is that you are triying to pass
obj.recipe
in onClick Function but you have not define theobj
variable. I don’t know if it something you are supposed to pass fromlist
variable from map’s prop or is it supposed to a differen variablei would also suggest you to bind you function like this:
onClick={() => clickFn(obj.recipe)}
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:
So to solve your issue you should update your code here:
I would also avoid declaring it inline by creating a handler function like this:
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: