You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Redux国家API搜索/筛选组件初始化与实现求助

实现国家API的搜索与筛选功能

问题背景

在基于Redux开发国家数据展示应用时,无法在现有代码基础上扩展实现搜索(按名称)和筛选(按大洲)功能,现有代码及尝试如下:


Store.js

import { applyMiddleware, combineReducers, createStore, compose } from 'redux'
import getCountries from './getCountries'

const initialState = {
        variable: {
            data: [],
    },
    filters: {
        searchPhrase: '',
        continent: ''
    }

}


const reducers = {
    variable: getCountries, // variablesReducer
}

const storeReducer = combineReducers(reducers)

const store = createStore(
    storeReducer,
    initialState,
    compose(applyMiddleware(), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) // przed deployem usunac
)
export default store

SearchPhraseRedux.js(注:推测实际文件名为getCountries.js,与Store导入路径匹配)

//SearchEngine
export const getData = ({variable}, filters) => {
    if(!filters.searchPhrase){
        return variable.data
    }   
    //Logic



    //export const getCountryByName = ({variable}, name) => 
    //variable.data.find(e=>e.name.common === name)
}


const reducerName = 'variable'
const createActionName = (name) => `app/${reducerName}/${name}`

export const SET_DATA = createActionName("SET_DATA")
export const setData = payload => ({payload, type: SET_DATA})


export default function reducer(statePart=[], action={}){
    switch(action.type) {
        case SET_DATA:
            return {...statePart, data: action.payload}
            default:
                return statePart
    }
}

countryContainer.js

import { connect } from "react-redux"
import { getData, setData} from '../../redux/getCountries'
import Countries from "./countries"


const mapStateToProps = (state) => ({
    data: getData(state),
})

const mapDispatchToProps = dispatch =>({
    setData: (value) => dispatch(setData(value)),
})


export default connect(mapStateToProps, mapDispatchToProps)(Countries)

countries.js

import React from 'react'
import { useEffect} from 'react'
import Axios from 'axios'
import '../../../src/index.css'
import {Link} from 'react-router-dom'


const Countries = ({data,setData}) => {

    const url = 'https://restcountries.com/v3.1/all'

    useEffect(() => {
      Axios.get(url)
      .then((rest) =>{
        setData(rest.data)
      })
    },[url, setData])

  return (
    <div>
        <section className='grid'>    
            {data.map((country) => {
                const {population, region, capital} = country
                    const {png} = country.flags
                        const {official, common} = country.name
                    return <article key={common}>
                        <div> 
                              <Link classsName='link' to={`/countries/${common}`}>
                              <img src ={png} alt={official}/> 
                              </Link>  
                            <div className='details'>
                              <h3>{common}</h3>
                              <h4>Population: <span>{population}</span></h4>
                              <h4>Region: <span>{region}</span></h4>
                              <h4>Capital: <span>{capital}</span></h4> 
                              </div>                                        
                        </div>
                            </article>
                        })}
        </section>   
    </div>
  )
}

export default Countries

此前尝试实现单个国家查询的代码:

export const getCountryByName = ({variable}, name) => variable.data.find(e=>e.name.common === name)

解决方案

从Redux状态管理和UI交互两部分修改,完成搜索与筛选功能:

1. 完善Redux状态体系

步骤1:创建filters专属Reducer

新建filtersReducer.js,用于管理搜索词和大洲筛选的状态更新:

const reducerName = 'filters'
const createActionName = name => `app/${reducerName}/${name}`

export const SET_SEARCH_PHRASE = createActionName('SET_SEARCH_PHRASE')
export const SET_CONTINENT = createActionName('SET_CONTINENT')

export const setSearchPhrase = payload => ({ type: SET_SEARCH_PHRASE, payload })
export const setContinent = payload => ({ type: SET_CONTINENT, payload })

const initialState = {
  searchPhrase: '',
  continent: ''
}

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case SET_SEARCH_PHRASE:
      return { ...state, searchPhrase: action.payload }
    case SET_CONTINENT:
      return { ...state, continent: action.payload }
    default:
      return state
  }
}
步骤2:更新Store注册filtersReducer

修改Store.js,将filtersReducer加入全局状态:

import { applyMiddleware, combineReducers, createStore, compose } from 'redux'
import getCountries from './getCountries'
import filtersReducer from './filtersReducer'

const initialState = {
  variable: {
    data: [],
  },
  filters: {
    searchPhrase: '',
    continent: ''
  }
}

const reducers = {
  variable: getCountries,
  filters: filtersReducer
}

const storeReducer = combineReducers(reducers)

const store = createStore(
  storeReducer,
  initialState,
  compose(applyMiddleware(), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
)
export default store
步骤3:完善getData选择器的筛选逻辑

修改getCountries.js中的getData函数,整合搜索与筛选逻辑:

// 搜索+筛选逻辑
export const getData = ({ variable, filters }) => {
  let filteredData = variable.data

  // 按搜索词模糊匹配(不区分大小写)
  if (filters.searchPhrase) {
    const lowerCasePhrase = filters.searchPhrase.toLowerCase()
    filteredData = filteredData.filter(country => 
      country.name.common.toLowerCase().includes(lowerCasePhrase) ||
      country.name.official.toLowerCase().includes(lowerCasePhrase)
    )
  }

  // 按大洲精确匹配
  if (filters.continent) {
    filteredData = filteredData.filter(country => country.region === filters.continent)
  }

  return filteredData
}

// 保留单个国家查询逻辑
export const getCountryByName = ({ variable }, name) => 
  variable.data.find(e => e.name.common === name)

const reducerName = 'variable'
const createActionName = (name) => `app/${reducerName}/${name}`

export const SET_DATA = createActionName("SET_DATA")
export const setData = payload => ({payload, type: SET_DATA})

// 修正初始状态为对象,避免解构错误
export default function reducer(statePart = { data: [] }, action = {}) {
  switch(action.type) {
    case SET_DATA:
      return {...statePart, data: action.payload}
    default:
      return statePart
  }
}

2. 更新容器组件传递状态与方法

修改countryContainer.js,将filters状态和更新方法传递给UI组件:

import { connect } from "react-redux"
import { getData, setData} from '../../redux/getCountries'
import { setSearchPhrase, setContinent } from '../../redux/filtersReducer'
import Countries from "./countries"

const mapStateToProps = (state) => ({
  data: getData(state),
  filters: state.filters
})

const mapDispatchToProps = dispatch =>({
  setData: (value) => dispatch(setData(value)),
  setSearchPhrase: (phrase) => dispatch(setSearchPhrase(phrase)),
  setContinent: (continent) => dispatch(setContinent(continent))
})

export default connect(mapStateToProps, mapDispatchToProps)(Countries)

3. 添加搜索与筛选UI交互

修改countries.js,新增搜索框和大洲选择器,并处理边界情况:

import React from 'react'
import { useEffect} from 'react'
import Axios from 'axios'
import '../../../src/index.css'
import {Link} from 'react-router-dom'

const Countries = ({ data, setData, filters, setSearchPhrase, setContinent }) => {
  const url = 'https://restcountries.com/v3.1/all'

  useEffect(() => {
    Axios.get(url)
      .then((rest) =>{
        setData(rest.data)
      })
  },[url, setData])

  // 获取所有唯一大洲选项
  const continents = [...new Set(data.map(country => country.region))].filter(Boolean)

  return (
    <div>
      {/* 搜索筛选栏 */}
      <div className="filters-bar">
        <input
          type="text"
          placeholder="搜索国家名称..."
          value={filters.searchPhrase}
          onChange={(e) => setSearchPhrase(e.target.value)}
          className="search-input"
        />
        <select
          value={filters.continent}
          onChange={(e) => setContinent(e.target.value)}
          className="continent-select"
        >
          <option value="">所有大洲</option>
          {continents.map(continent => (
            <option key={continent} value={continent}>{continent}</option>
          ))}
        </select>
      </div>

      <section className='grid'>    
        {data.length > 0 ? (
          data.map((country) => {
            const {population, region, capital} = country
            const {png} = country.flags
            const {official, common} = country.name
            return <article key={common}>
              <div> 
                <Link className='link' to={`/countries/${common}`}>
                  <img src={png} alt={official}/> 
                </Link>  
                <div className='details'>
                  <h3>{common}</h3>
                  <h4>Population: <span>{population.toLocaleString()}</span></h4>
                  <h4>Region: <span>{region}</span></h4>
                  <h4>Capital: <span>{capital?.[0] || '无'}</span></h4>
                </div>                                        
              </div>
            </article>
          })
        ) : (
          <p>暂无匹配数据...</p>
        )}
      </section>   
    </div>
  )
}

export default Countries

关键说明

  • 搜索逻辑支持不区分大小写的模糊匹配,覆盖国家常用名和官方名
  • 筛选逻辑直接匹配API返回的region字段,保证数据一致性
  • 处理了无首都国家、数据加载中、无匹配结果等边界情况
  • 修正原代码中的拼写错误(classsName→className)
  • 添加人口数字千分位格式化,提升可读性

内容的提问来源于stack exchange,提问作者bartek.zwolinski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 06:45:31