Is it possible to extract an attribute value from a parent element using ReactJS?

https://i.stack.imgur.com/OXBB7.png

Whenever I select a particular button, my goal is to capture the {country} prop that is linked to it. I attempted the following approach

import React, { useState, useEffect } from 'react'
import axios from 'axios'


// ====================================================================[SEARCH-BAR]=======================================================
// search component
const SearchBar = (props) => {
    // console.log(props);
    const { searchString, searchOnChangeEventHandler } = props
    return (
        <>
            <form>
                <label>Search </label>
                <input type='text' placeholder='type to search...' value={searchString} onChange={searchOnChangeEventHandler} />
            </form>
        </>
    )
}

// ================================================================[COUNTRY_CARD]==========================================================
// countryCard component
const CountryCard = (props) => {
    console.log(props);
    return (
        <div>
            <p>countryName</p>
            <p>capital</p>
            <p>population</p>
            <p>languages</p>
            <ul>
                <li>item</li>
                <li>item</li>
            </ul>
            <p>image flag</p>
        </div>
    )
}


// ===================================================================[DISPLAY]===========================================================
// display component
const Display = (props) => {
    const [showCountryCard, setShowCountryCard] = useState(false)
    const [thisCountry, setThisCountry] = useState({})
    // console.log(props);
    const { countries, searchString } = props
    // console.log(countries);
    // eslint-disable-next-line eqeqeq

    // searchString empty
    if (searchString == false) {
        return (
            <>
                <div>
                    <span>Type in SearchBar for a country...</span>
                </div>
            </>
        )
    }

    // to count number of matches
    const filteredResultsCount = countries.filter(country => country.name.toLowerCase().includes(searchString.toLowerCase())).length
    // function to filterCountries
    const filteredResults = (searchString, countries) => countries.filter(country => {
        return country.name.toLowerCase().includes(searchString.toLowerCase())
    })

    // RENDER CONDITIONS
    // searchString return <= 10 matches && >1 match
    // event handler for show-btn
    const showCardEventHandler = (event) => {
        console.log(event.target.parentElement);
        setShowCountryCard(!showCountryCard)
    }
    if (filteredResultsCount <= 10 && filteredResultsCount > 1) {
        return (
            <>
                <ul>
                    {
                        filteredResults(searchString, countries).map(country =>
                            <li
                                key={country.numericCode}
                                country={country}
                            >
                                <span>{country.name}</span>
                                <button
                                    value={showCountryCard}
                                    onClick={showCardEventHandler}
                                >show</button>
                            </li>
                        )
                    }
                </ul>
                {
                    showCountryCard ? <p>show country card</p> : null
                }
            </>
        )
    }
    // searchString returns >10 matches
    if (filteredResultsCount > 10) {
        return (
            <span>{filteredResultsCount} matches!, please refine your search...</span>
        )
    }
    // searchString returns ===1 match
    if (filteredResultsCount === 1) {
        return (
            <>
                {
                    filteredResults(searchString, countries).map(country => <CountryCard key={country.numericCode} country={country} />)
                }
            </>

        )
    }
    // invalid searchString
    if (filteredResultsCount === 0) {
        return (
            <span><strong>{filteredResultsCount} matches!</strong> please refine your search...</span>
        )
    }
}

// ===================================================================[APP]==============================================================
// app component
const App = () => {
    // to store countries
    const [countries, setCountries] = useState([])

    // to fetch data from 
    const url = 'https://restcountries.eu/rest/v2/all'
    useEffect(() => {
        // console.log('effect');
        axios
            .get(url)
            .then(response => {
                // console.log('promise fulfilled');
                const countries = response.data
                // array of objects
                setCountries(countries)
            })
    }, [])
    // console.log('countries', countries.length);
    // console.log(countries);

    // to store search string
    const [searchString, setSearchString] = useState('')
    // event handler search input
    const searchOnChangeEventHandler = (event) => setSearchString(event.target.value)

    return (
        <>
            <h1>Countries Data</h1>
            <SearchBar searchString={searchString} searchOnChangeEventHandler={searchOnChangeEventHandler} />
            <br />
            <Display countries={countries} searchString={searchString} />
        </>
    )
}

export default App

Please review the <Display/> component, particularly focusing on this section

    const showCardEventHandler = (event) => {
        console.log(event.target.parentElement);
        setShowCountryCard(!showCountryCard)
    }
    if (filteredResultsCount <= 10 && filteredResultsCount > 1) {
        return (
            <>
                <ul>
                    {
                        filteredResults(searchString, countries).map(country =>
                            <li
                                key={country.numericCode}
                                country={country}
                            >
                                <span>{country.name}</span>
                                <button
                                    value={showCountryCard}
                                    onClick={showCardEventHandler}
                                >show</button>
                            </li>
                        )
                    }
                </ul>
                {
                    showCountryCard ? <p>show country card</p> : null
                }
            </>
        )
    }

I aim to display a list of countries when there are more than 10 and allow users to click on a specific country, triggering the rendering of the <CountryCard/> component. If only one matching result is found during the search, the country card component will be displayed directly. The second feature currently works.
After refactoring as described above, the first feature now works, but I am puzzled as to why. Therefore, I have elaborated further in this post. This is the component being rendered, and I am now passing the country prop onClick, like so

    if (filteredResultsCount <= 10 && filteredResultsCount > 1) {
        return (
            <>
                <ul>
                    {filteredResults(searchString, countries).map((country) => (
                        <li key={country.numericCode} country={country}>
                            <span>{country.name}</span>
                            <button
                                value={showCountryCard}
                                onClick={() => toggleCardEventHandler(country)}>
                                {showCountryCard ? 'hide' : 'show'}
                            </button>
                        </li>
                    ))}
                </ul>
                {showCountryCard ? <CountryCard country={country} /> : null}
            </>
        );
    }

The event handler is defined as follows

    const toggleCardEventHandler = (country) => {
        // console.log(country);
        setShowCountryCard(!showCountryCard);
        setCountry(country)
    };

This implementation works correctly. My inquiry is, why does changing the eventHandler

onClick={toggleCardEventHandler(country)}
cause it to fail, even though it should be accessible through closure?

Furthermore, if I modify the code as shown below

onClick={() => {
    toggleCardEventHandler()
    setCountry(country)
}}

The code functions as intended, but which method is preferable for passing the value to toggleCardEventHandler() and setting the country? Is it better to do it this way or the former?

Answer №1

It seems like you're looking to pass the country.name to your showCardEventHandler function. Make sure to update the showCardEventHandler so that it accepts the event and country name:

const showCardEventHandler = (event, countryName) => {
  console.log(countryName);
  setShowCountryCard(!showCountryCard)
}

Now, don't forget to pass the countryname to the function:

<li
  key={country.numericCode}
  country={country}
>
 <span>{country.name}</span>
 <button
   value={showCountryCard}
   onClick={e => showCardEventHandler(e, country.name)}
 >show</button>
</li>

Since the event is not being used in showCardEventHandler, you can remove it from the signature:

const showCardEventHandler = (countryName) => {}
and call the function simply with:
onClick={() => showCardEventHandler(country.name)}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Redux Thunk failing to trigger the return statement

Despite searching on Stack Overflow, I have not been able to find a solution to my current issue. My problem lies in using redux thunk to dispatch an action, as the return statement inside my thunk function does not seem to be firing. I attempted to use r ...

Updating subdata in an array using Reactjs handleChange

I am facing an issue with my handleChange function. While I can easily change data in the same directory without any problem, I'm struggling to update sub-data. I would like to find a clean solution for this without having to add extra functions withi ...

"Exploring ways to reattempt a route request upon encountering the $stateNotFound event within AngularUI Router

Managing a large AngularJS application can be quite challenging when it comes to splitting it into functional modules. Currently, all the modules are loaded on the initial page load as they are bundled into a single JavaScript file. However, I am looking t ...

What could be the reason my code isn't successfully performing addition within the input field?

As a novice, I am practicing by attempting to retrieve a number from a text field, prompting the user to click a button that adds 2 to that number, and then displaying the result through HTML. However, I keep encountering an issue where NaN is returned whe ...

Is it feasible to convert a Google Drive spreadsheet into JSON format without needing the consent screen?

I'm working on incorporating a JSON feed directly from a private spreadsheet (accessible only via link) onto my website. In order to do this, I must create a new auth token using OAuth 2.0, which is not an issue. However, the Google Sheets API v4 mand ...

How to apply styling to a specific portion of text within a list element using Vue.js

Despite my best efforts, I am struggling to style only the word "healthy" within the 'It is healthy!' string using computed properties, watchers, and methods in vuejs. I'm at a loss for how to achieve this unique styling. <template> ...

Clickable link unresponsive on parallax-enhanced webpage

Currently, I am utilizing Zurb foundation's Manifesto theme for creating a parallax scrolling landing page. The anchor tag is essential for the scrolling effect on this page, causing a conflict when regular anchor links are included. Here is the HTML ...

What could be causing the issue with AJAX not running in a Python Django deployment on Heroku

My Django application is successfully deployed on Heroku, but I'm facing an issue with executing Ajax in the template. The Ajax functionality works perfectly fine on my local machine, however, it's not working on Heroku. I've included a snip ...

"Utilize React to dynamically render Material UI icons in a web application

Hey there, I could use some help with a certain issue. In my project, I'm utilizing Material UI icons and they are set up in a way where I have to import them like this: import {Muicon } from '@/lib/material'; ... <Muicon.Visibility /& ...

What is the best way to write an SQL query to safely insert a record into a table with a dynamic name?

I'm working on a function that can insert a record into a table in PostgreSQL. The catch is that the table name needs to be a parameter for the function, and the column names are determined dynamically. To ensure protection against SQL Injection, I am ...

Step-by-step guide on integrating StyleX into your fresh React project

As I delve into my new project, incorporating StyleX has proven to be a bit challenging especially when working with NextJS. I find myself grappling with configuring the "next.config.js" file without causing conflicts with the existing "babel.config.js" f ...

Display the iframe website without it being visible to the user

Is there a way to load a separate website, such as a Wikipedia article, in an iframe on a webpage without freezing up the whole page when it becomes visible after clicking a "show" button? And if not, how can we display a loading gif while the iframe is ...

Tips for incorporating styles into react-pdf Document

I attempted to modify the width using this code, but it isn't working as expected <Document style={{width:"100px"}} file="123.pdf" ...

Leveraging React Native to position a view absolutely in the center of the screen without obstructing any other components

How can I center an image inside a view in the middle of the screen using position: "absolute"? The issue is that the view takes up 100% of the width and height of the screen, causing all components underneath it (such as input fields and buttons ...

Optimal method for linking jQuery ajax requests to transfer data

Handling several asynchronous ajax calls in a specific order with information passing between them can be quite challenging. The current approach, even with just three API calls, can be cumbersome. Trying to manage five API calls makes it nearly impossible ...

the reason behind the peculiar behavior of angularjs ng-include

I am attempting to utilize an ng-template to iterate through my args in order to create an indented menu content. Unfortunately, I have encountered issues with ng-include not working as expected. I have tried adding a quote but it still does not work. For ...

Error message: The function URL.createObjectURL is not recognized in this context | Issue with Antd charts

Currently, I am working on integrating charts from antd into my TypeScript application. Everything runs smoothly on localhost, but as soon as I push it to GitHub, one of the tests fails: FAIL src/App.test.tsx ● Test suite failed to run TypeError: ...

Using jQuery's setInterval to consistently refresh the value of a data attribute

I am struggling to dynamically update the data-left value of a div with the class name "tw_marquee_scroller" every 1 second. The intended behavior is for the value to increment by 10 each time, starting at 10 and increasing by 10 in subsequent seconds. H ...

Make an axios request multiple times equal to the number of items in the previous response

In my project, I am using the axios library to convert addresses into their respective coordinates. First, I fetch a list of addresses from an API. Next, I take the RESPONSE object and use Google API to convert each address to coordinates. Finally, I wan ...

Tips for showcasing unique keywords in Ace Editor within the Angular framework

Can anyone help me with highlighting specific keywords in Angular using ace-builds? I've tried but can't seem to get it right. Here's the code snippet from my component: Check out the code on Stackblitz import { AfterViewInit, Component, ...