反应按钮以显示元素

时间:2021-06-04 23:20:40

标签: reactjs

我正在从 Restcountries API 中提取国家/地区,如果数组的当前状态有超过一个或少于或等于 10 个国家/地区,我想列出国家/地区名称以及每个国家/地区旁边的“显示”按钮一。显示按钮应该显示我的 Country 函数的返回(渲染)中的内容。在 App 函数中,我为名为 handleViewButton 的按钮编写了一个处理程序。我对如何在 Countries 条件语句中过滤 else 函数中的元素以显示 Country 感到困惑。我尝试将 handleViewButton 传递给 Button 函数,但出现错误“Uncaught TypeError: newSearch.toLowerCase is not a function”。我真的只想触发 Country 函数来显示按下的国家/地区按钮。

App.js

import React, { useState, useEffect } from 'react';
import './App.css';
import axios from 'axios';

const Country = ({country}) => {
    return (
        <>
            <h2>{country.name}</h2>
                <p>capital {country.capital}</p>
                <p>population {country.population}</p>
            <br/>
            <h3>languages</h3>
                {country.languages.map(language => <li key={language.name}>{language.name}</li>)}
            <br/>
            <img src={country.flag} alt="country flag" style={{ width: '250px'}}/>
        </>
    );
}

const Countries = ({countries, handleViewButton}) => {    
    const countriesLen = countries.length;
    console.log(countriesLen)

    if (countriesLen === 0) {
        return (
           <p>Please try another search...</p>
        );
    } else if (countriesLen === 1) {
        return (
           <ul>
                {countries.map((country, i) => <Country key={i} countriesLen={countriesLen} country={country}/>)}
           </ul>
        );
    } else if (countriesLen > 10) {
        return (
            <div>
                <p>Too many matches, specify another filter</p>
            </div>
        );
    } else {
        return (
            <ul>
                {countries.map((country, i) => <li key={i}>{country.name}<Button handleViewButton={handleViewButton}/></li>)}
            </ul>
        )
    };
};

const Button = ({handleViewButton}) => {
    return (
        <button onClick={handleViewButton}>Show</button>
    );
};

const Input = ({newSearch, handleSearch}) => {
    return (
        <div>
            find countries <input value={newSearch} onChange={handleSearch}/>
        </div>
    );
};

function App() {
    const [countries, setCountries] = useState([]);
    const [newSearch, setNewSearch] = useState('');

    const handleSearch = (event) => {
        const search = event.target.value;
        setNewSearch(search);
    };

    const handleViewButton = (event) => {
        const search = event.target.value;
        setNewSearch(countries.filter(country => country === search));
    };

    const showCountrySearch = newSearch
        ? countries.filter(country => country.name.toLowerCase().includes(newSearch.toLowerCase()))
        : countries;

    useEffect(() => {
        axios
            .get('https://restcountries.eu/rest/v2/all')
            .then(res => {
                setCountries(res.data);
                console.log('Countries array loaded');
            })
            .catch(error => {
                console.log('Error: ', error);
            })
    }, []);

    return ( 
        <div>
            <Input newSearch={newSearch} handleSearch={handleSearch}/>
            <Countries countries={showCountrySearch} handleViewButton={handleViewButton}/>
        </div>
    );
};

export default App;

enter image description here

2 个答案:

答案 0 :(得分:1)

您可以使用 displayCountry 来处理应显示的国家/地区。大多数情况下,您会使用 ID,但我在这里使用 country.name,因为它应该是唯一的。

然后您将使用 matchedCountry 查找您的国家/地区列表。

之后,使用 onHandleSelectCountry 选择给定的国家/地区。如果它已被选中,那么您可以设置为 null 以取消选中。

最后,您将有条件地呈现您的 matchedCountry

const Countries = ({countries}) => {
    const [displayCountry, setDisplayCountry] = useState(null); 
    const countriesLen = countries.length;
    
    const matchedCountry = countries.find(({ name }) => name === displayCountry); 

    const onHandleSelectCountry = (country) => {
      setDisplayCountry(selected => { 
        return selected !== country.name ? country.name : null
      })
    }


    if (countriesLen === 0) {
        return (
           <p>Please try another search...</p>
        );
    } else if (countriesLen === 1) {
        return (
           <ul>
                {countries.map((country, i) => <Country key={i} countriesLen={countriesLen} country={country}/>)}
           </ul>
        );
    } else if (countriesLen > 10) {
        return (
            <div>
                <p>Too many matches, specify another filter</p>
            </div>
        );
    } else {
        return (
          <>
            <ul>
                {countries.map((country, i) => <li key={i}>{country.name}<Button handleViewButton={() => onHandleSelectCountry(country)}/></li>)}
            </ul>
            { matchedCountry && <Country countriesLen={countriesLen} country={matchedCountry}/> }
          </>
        )
    };
};

答案 1 :(得分:0)

我只能帮助指出一些指导方针。

First:按钮没有 value 属性。因此,您从 event.target.value 得到的总是空白。

const Button = ({handleViewButton}) => {
return (
    <button onClick={handleViewButton}>Show</button>
);};

First->Suggestion:给按钮添加值,当然需要传入值。

const Button = ({handleViewButton, value}) => {
return (
    <button onClick={handleViewButton} value={value}>Show</button>
);};

第二:对于您的问题“未捕获的类型错误:newSearch.toLowerCase 不是函数”。 Filter 总是返回一个数组,而不是单个值。如果你使用控制台或一些沙箱 [1,2,3].filter(x=>x===2) 你会得到 [2] 而不是 2。

const handleViewButton = (event) => {
    const search = event.target.value;
    setNewSearch(countries.filter(country => country === search));
};

第二个->建议:更改它以获取数组中的第一个元素,因为国家(逻辑上)是唯一的。

const result = countries.filter(country => country === search)
setNewSearch(result.length>0?result[0]:"");

一个更好的数组方法是 find,它总是返回第一个结果并作为一个值。例如。 [1,2,2,3].find(x=>x===2) 你会得到 2 而不是 [2,2] 或 [2]。

countries.find(country => country === search)