Home > Mobile >  React: Passing a dictionary of functions but somehow failing to access it correctly
React: Passing a dictionary of functions but somehow failing to access it correctly

Time:12-05

I have an App component where I define two functions A and B.

I am then passing these two functions to a Card component as follow

          <Card 
            key = {el.key} 
            item={el} 
            onPress={{
                select : ()=>A(el.key),
                discard : ()=>B(el.key)
          }}
          />

however, for some reason, when inside my Card i do something like

    ....
    <div 
        className={className} 
        onClick={props.onPress.select}>
    </div>
    ...

I get this nasty error


react-dom.development.js:18687 The above error occurred in the <Card> component:

    at Card (http://localhost:5174/src/components/Card.jsx?t=1670102247232:33:48)
    at div
    at div
    at div
    at App (http://localhost:5174/src/App.jsx?t=1670102247232:24:31)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
react-dom.development.js:26923 Uncaught TypeError: Cannot read properties of undefined (reading 'select')
    at Card (Card.jsx:28:32)
    at renderWithHooks (react-dom.development.js:16305:18)
    at mountIndeterminateComponent (react-dom.development.js:20074:13)
    at beginWork (react-dom.development.js:21587:16)
    at beginWork$1 (react-dom.development.js:27426:14)
    at performUnitOfWork (react-dom.development.js:26557:12)
    at workLoopSync (react-dom.development.js:26466:5)
    at renderRootSync (react-dom.development.js:26434:7)
    at recoverFromConcurrentError (react-dom.development.js:25850:20)
    at performConcurrentWorkOnRoot (react-dom.development.js:25750:22)


what am I doing wrong?

Here below both the App code and the Card code

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import Card from './components/Card'
import data from "./assets/data.json"
import {nanoid} from "nanoid"

function App() {
  const [people, setPeople] = useState(data['people'].map(
    el=>{
      return {
        ...el,
        'key' : nanoid(),
      }
    }
  ))

  const [myCard, setMyCard] = useState(
    {
      "name":"Name and Surname",
      "img":"https://i.picsum.photos/id/1070/200/300.jpg?hmac=dJNTYlLwT_0RupxbJNbw5Wj-q2cCTB4Xh-GqWRofIIc",
      "description": "This is a silly descriptionhis is a silly descriptionhis is a silly description",
      'key' : nanoid(),
      "selected":false,
      "active":true,
    }
  )


  function SelectCard(CardKey){
    console.log('SelectCard')
    setPeople(oldPeople=>{
      return oldPeople.map(el=>{

        return el.key === CardKey
                ? { ...el, 'state': el.state === 'active'?'selected': 'active'}
                : { ...el, 'state':'active'}
          })
    })
  }

  function DiscardCard(CardKey){
    console.log('DiscardCard')
    setPeople(oldPeople=>{
      return oldPeople.map(el=>{
        return el.key === CardKey
                ? { ...el, 'state': el.state === 'discarded'?'active': 'discarded'}
                : { ...el}
          })
    })
  }

  const cards = people.map(el=>{
    return <Card 
            key = {el.key} 
            item={el} 
            onPress={{
                select : ()=>SelectCard(el.key),
                discard : ()=>DiscardCard(el.key)
          }}
          />
  })

  
  return (
    <div className="App">
      <div className='container'>
        <div className='left'>
          <div className='cards'>
            {cards}
          </div>
        </div>
        <div className = 'right'>
          <h4>You are: </h4>
          <Card key = {myCard.key} item={myCard}/>
        </div>
     </div>
     
    </div>
  )
}

export default App

and the card

import { useState } from 'react'

function Card(props) {
  //const [count, setCount] = useState(0)

  function getClassName(state) {
    switch (state) {
      case "active":
        return "";
      case "selected":
        return "overlay-selected";
      case "discarded":
        return "overlay-discarded";
      case "complete":
        return "overlay-complete";
      default:
        return "";
    }
  }



  const className = `card ${getClassName(props.item.state)}`

  return (
    <div 
        className={className} 
        onClick={props.onPress.select}>
        <img className='card-img' alt='Card Image' src={props.item.img} />
        <h3 className='card-title'>{props.item.name} </h3>
        { props.item.state === 'selected' ? 
        
            <div className='card-cta'>
                <button 
                    className='btn btn-back'
                    onClick={ props.item.selected ?  (event)=>
                        { 
                            props.onPress.select
                            //event.stopPropagation()
                        }
                         : ()=>{}}
                >Back</button>
                <button 
                    className='btn btn-discard'
                    onClick={ 
                        (event) =>{
                            //event.stopPropagation()
                            props.onPress.discard 
                            console.log('called me')
                            }
                        }
                >Discard</button>
            </div>
        : 
        <p className='card-description'>{props.item.description} </p>  
    
        }
        
    </div>
  )
}

export default Card

CodePudding user response:

This happens for the

<h4>You are: </h4>
<Card key={myCard.key} item={myCard} />

where you do not pass any onPress property.

So you need to either pass dummy methods for this case as well, or use the optional chaining operator ?., like so props.onPress?.select


Additionally, you should actually call the methods from your buttons

so

(event) =>{
  //event.stopPropagation()
  props.onPress.discard
  console.log('called me')
}

should become

(event) =>{
  //event.stopPropagation()
  props.onPress.discard() // <-- added the () here
  console.log('called me')
}
  • Related