返回

React 通用解决方案——筛选与路由联动,让你的筛选器效果更佳

前端

背景介绍

在前后端开发不分离的「远古时代」,页面的筛选器操作筛选一般会同步变更URL,触发浏览器刷新,从而正确呈现「选定筛选值」的筛选器和根据筛选器过滤后的列表数据。这种渲染模式的流程大致如下:

  1. 用户在筛选器上选择一个值。
  2. 筛选器组件将选定的值发送给父组件。
  3. 父组件将选定的值存储在状态中。
  4. 父组件使用选定的值更新URL。
  5. 浏览器刷新页面。
  6. 组件重新渲染,并使用新的URL中的选定值来显示筛选器和列表数据。

这种渲染模式虽然简单易懂,但存在一些缺点:

  • 页面闪烁: 由于浏览器需要刷新页面,因此在筛选器操作后会看到页面闪烁。
  • 丢失状态: 如果用户在筛选器操作后刷新页面,则筛选器状态将丢失。
  • URL不直观: URL中包含筛选器值,这使得URL不直观,也不利于搜索引擎优化。

React中的筛选和路由联动

为了解决上述问题,我们可以使用React中的路由和状态管理来实现筛选和路由的联动。

1. 使用路由管理筛选器状态

我们可以使用React Router来管理筛选器状态。当用户在筛选器上选择一个值时,我们可以使用history.push()方法来更新URL,并将选定的值作为查询参数添加到URL中。例如:

import { useHistory } from "react-router-dom";

const Filter = () => {
  const history = useHistory();

  const handleFilterChange = (value) => {
    history.push({
      pathname: "/products",
      search: `?filter=${value}`,
    });
  };

  return (
    <select onChange={(e) => handleFilterChange(e.target.value)}>
      <option value="all">All</option>
      <option value="category1">Category 1</option>
      <option value="category2">Category 2</option>
    </select>
  );
};

2. 使用状态管理工具管理筛选器状态

我们也可以使用状态管理工具来管理筛选器状态。例如,我们可以使用Redux来存储筛选器状态。当用户在筛选器上选择一个值时,我们可以使用Redux的dispatch()方法来更新筛选器状态。例如:

import { useDispatch } from "react-redux";

const Filter = () => {
  const dispatch = useDispatch();

  const handleFilterChange = (value) => {
    dispatch({
      type: "SET_FILTER",
      payload: value,
    });
  };

  return (
    <select onChange={(e) => handleFilterChange(e.target.value)}>
      <option value="all">All</option>
      <option value="category1">Category 1</option>
      <option value="category2">Category 2</option>
    </select>
  );
};

3. 使用自定义组件管理筛选器状态

如果我们不想使用路由或状态管理工具,我们也可以使用自定义组件来管理筛选器状态。例如,我们可以创建一个Filter组件来存储筛选器状态。当用户在筛选器上选择一个值时,我们可以使用setState()方法来更新筛选器状态。例如:

class Filter extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filter: "all",
    };

    this.handleFilterChange = this.handleFilterChange.bind(this);
  }

  handleFilterChange(value) {
    this.setState({
      filter: value,
    });
  }

  render() {
    return (
      <select onChange={this.handleFilterChange}>
        <option value="all">All</option>
        <option value="category1">Category 1</option>
        <option value="category2">Category 2</option>
      </select>