react-redux - 如何实现 React Redux 搜索和复选框过滤器
问题描述
我正在尝试在我的反应项目中实现搜索/过滤器,但似乎无法正确处理。 截屏
这是用户应该能够做到的:
- 按文本过滤
- 使用复选框按位置过滤
- 或按工作类别过滤
我现在拥有的是我可以按文本和复选框进行过滤,但我希望用户能够按文本搜索,例如“达拉斯”、“信息技术”、“销售”,然后按类别进一步过滤或使用从文本搜索返回的数据按位置。
这是我的设置:
- 按文本搜索使用 reducer 按搜索文本过滤数组
- 检查位置或类别复选框将值存储在类别或关键字数组中
- 在组合减速器文件中,我过滤存储中的作业数组并使用重新选择记住它。
我的搜索文本和复选框功能有一些小故障。按位置过滤复选框功能本身可以正常工作;however, when a checkbox is selected, and I enter search text, checkbox gets deselected. 当在输入字段中输入文本时,我希望复选框仍然被选中并显示在窗口中。
我是新手,任何帮助、建议或对此功能的更好方法将不胜感激。
搜索表格.JS:
import React, {Component} from 'react';
//import {FormGroup, FormControl, InputGroup} from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { searchJobs, getJobs } from '../actions/jobaction';
import {selectKeywordCity,deselectKeywordCity} from '../actions/selectDeselectCityAction';
import {selectKeywordCategory,deselectKeywordCategory} from '../actions/selectDeselectCategoryAction';
class Search extends Component{
constructor(props){
super();
this.state = {
keyword:'',
checked:[]
};
this.handleChk = this.handleChk.bind(this);
}
handleInput = (event) => {
this.setState({
keyword: event.target.value
});
this.props.searchJobs(this.state.keyword);
}
/* handleSearch = (event) => {
event.preventDefault();
this.props.getJobs(this.state.keyword);
} */
clearForm = () => {
this.inputSearch.value = "";
this.setState({
keyword: this.inputSearch.value
});
this.props.getJobs();
}
handleChk = (e, keyword, type) => {
if(type==='city'){
e.target.checked ? this.props.selectKeywordCity(keyword) : this.props.deselectKeywordCity(keyword);
}else if(type==='category'){
e.target.checked ? this.props.selectKeywordCategory(keyword) : this.props.deselectKeywordCategory(keyword);
}
}
//Render Cities
renderCities(data){
if(data){
const cities = Object.keys(data).map(el => {
return data[el].city
}).sort()
.reduce((city,name) => {
const cityCount = city[name] ? city[name] + 1 : 1;
return{
...city, [name]:cityCount,
};
},{});
return Object.keys(cities).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'city')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{cities[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
//Render Job Categories
renderCategories(data){
if(data){
const categories = Object.keys(data).map(el => {
return data[el].category
}).sort()
.reduce((category,name) => {
const categoryCount = category[name] ? category[name] + 1 : 1;
return{
...category, [name]:categoryCount,
};
},{});
return Object.keys(categories).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'category')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{categories[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
render(){
let closeBtn = null;
if(this.state.keyword){
closeBtn = (<i className="fa fa-times" onClick={this.clearForm}></i>);
}
return(
<div>
<div className="side-search">
<i className="fa fa-search"></i>
<input type="text"
placeholder="Keyword Search"
ref={el => this.inputSearch = el}
onChange={this.handleInput}
value={this.state.keyword}
/>
{closeBtn}
</div>
<div className="chk_boxes">
<h4>Location</h4>
<ul>
{
this.renderCities(this.props.jobs)
}
</ul>
</div>
<div className="chk_boxes">
<h4>Category</h4>
<ul>
{
this.renderCategories(this.props.jobs)
}
</ul>
</div>
</div>
)
}
}
function mapStateToProps(state){
return{
jobs: state.jobs.jobs
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({searchJobs,
getJobs,
selectKeywordCity,
deselectKeywordCity,
selectKeywordCategory,
deselectKeywordCategory},
dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(Search);
工作:
import axios from 'axios';
const FETCH_URL = 'http://localhost:3006';
/* GET ALL JOB LISTINGS */
export function getJobs(){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs`)
.then(function(response){
dispatch({type:'GET_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'GET_JOBS_ERROR', payload:err});
})
}
}
/* GET ALL JOB LISTINGS */
export function searchJobs(keyword){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs?q=${keyword}`)
.then(function(response){
dispatch({type:'SEARCH_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'SEARCH_JOBS_ERROR', payload:err});
})
}
}
工作清单减少器:
export function Listings (state={}, action){
switch(action.type){
case 'GET_JOBS':
return{...state, jobs:action.payload}
case 'SEARCH_JOBS':
return{...state, jobs:action.payload}
default:
return state;
}
}
SELECTDESELECT(复选框)减速器:
const INITIAL_STATE = {
keywords:[]
}
export function filterCity(state=INITIAL_STATE, action){
switch(action.type){
case 'SELECT_CITY':
return{
keywords:[...state.keywords, action.payload]
}
case 'DESELECT_CITY':
const currentKeywordToDelete = [...state.keywords];
const indexOfKeyword = currentKeywordToDelete.findIndex(function(keyword){
return keyword === action.payload
});
return{
keywords:[...state.keywords.slice(0,indexOfKeyword), ...currentKeywordToDelete.slice(indexOfKeyword+1) ]
}
default:
return state;
}
}
组合减速机:
import {combineReducers} from 'redux';
import {createSelector} from 'reselect';
import {Listings} from './listingsReducer';
import {filterCity} from './select-deselect-city';
import {filterCategory} from './select-deselect-category';
const reducer = combineReducers({
jobs: Listings,
keywords: filterCity,
category: filterCategory
});
export const selectJobList = (state) => state.jobs.jobs;
export const selectCities = (state) => state.keywords.keywords;
//export const selectCategories = (state) => state.categories.categories;
export const selectFilteredJobs = createSelector(
selectJobList,selectCities,
(jobs,cities) => {
if(cities && cities.length > 0){
const filteredData = Object.keys(jobs)
.filter(key => cities.includes(jobs[key].city))
.reduce((obj, key) => {
return [...obj, {...jobs[key]}]
}, []);
//console.log(filteredData);
return filteredData;
}else{
return jobs
}
}
)
export default reducer;
JSON数据:
{
"jobs": [
{
"id": 1,
"title": "Financial Analyst II",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. .",
"category": "Sales",
"dept": "Finance",
"date": "07/11/2018",
"experience": ["Bachelors Degree","Five years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "10,000"
},
{
"id": 2,
"title": "Application Support Speacialist",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.",
"category": "Information Technology",
"dept": "Accounting",
"date": "07/10/2018",
"experience": ["Bachelors Degree","Two years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "20,000"
}
}
解决方案
推荐阅读
- html - 创建交错布局
- r - 无法在 RSelenium Mac 上的 rsDriver 上打开 Firefox
- sql - 比较三列数据并在第四列设置标志
- javascript - 使用 A 标签动态携带数据以在模态表单中使用
- loops - 在ansible中构建group_vars时遍历列表
- django - 将值传递给 api 并返回特定对象
- amazon-web-services - 配置 SNS 主题以通过 SES 的特定配置集发送电子邮件
- python - 在 4600000 行数据上训练 keras 模型时出现内存错误
- amazon-web-services - Logstash 在与 AWS Elasticservice 连接时抛出 401
- html - Beautifulsoup 删除了结束标签,这反过来又破坏了 jinja 模板