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
相关产品推荐
相关产品推荐

