主页关于链接归档灯泡

React 给归档页面添加分类功能

代码

给归档页加了个分类展示功能,简单分享下思路。


主体思路

  1. 设置状态变量: 在组件中声明一个 selectedCategory 状态变量和对应的更新函数 setSelectedCategory,用于存储和更新选择的分类。
  2. 处理分类点击事件: 创建 handleCategoryClick 函数,接收一个 categoryId 参数,并在函数体内调用 setSelectedCategory 将该参数设为当前选择的分类。
  3. 筛选文章列表: 使用 filteredPostsByYear 变量存储经过筛选后的文章列表。利用 map 函数遍历 postsByYear 数组,对每个年份的文章列表进行过滤。过滤条件是:如果有选择的分类(selectedCategory 不为空),则只保留属于该分类的文章;如果没有选择分类,则保留所有文章。最后,使用 filter 函数过滤掉没有文章的年份。
  4. 获取所有分类: 创建 allCategories 变量,它是一个存储所有文章分类的数组。通过对 allPostsData 中的文章进行扁平化处理,提取出所有的分类,并使用 Set 数据结构去重。
  5. 重置选择的分类: 编写 handleResetCategory 函数,将 selectedCategory 的值设为空字符串,表示显示所有文章。
  6. 页面展示: 在页面上展示分类功能的相关元素。包括显示当前选择分类下的文章数量或总文章数量的提示文本,分类按钮列表以及按年份展示的文章列表。

首先过一遍我的原代码:

import Head from 'next/head';
import Link from 'next/link';
import Layout from '../../components/layout';
import Date from'../../components/date';
import { getSortedPostsData } from '../../lib/posts';

// 获取静态数据
export async function getStaticProps() {
  // 获取所有文章数据并按日期排序
  const allPostsData = getSortedPostsData();
  // 提取所有不重复的年份
  const years = Array.from(new Set(allPostsData.map(post => post.date.slice(0, 4))));
  // 按年份分类文章
  const postsByYear = years.map(year => ({
    year: year,
    posts: allPostsData.filter(post => post.date.slice(0, 4) === year)
  }));

  return {
    props: {
      postsByYear,
      allPostsData,
    },
  };
}

export default function Archives({ postsByYear, allPostsData }) {
  return (
    <Layout>
      {/* 显示总文章数量 */}
      <p>目前共有 {allPostsData.length} 篇文章。</p>
      
      {/* 根据年份分类展示文章 */}
      {postsByYear.map(year => (
        <div key={year.year}>
          {/* 显示年份 */}
          <h3>{year.year}</h3>
          <ul>
            {/* 展示每篇文章的标题、日期和分类 */}
            {year.posts.map(({ id, date, title, categories }) => (
              <li key={id}>
                {/* 链接到文章页面 */}
                <Link href={`/posts/${id}`}>
                  {title}
                </Link>
                <small>
                  {/* 显示文章日期和分类 */}
                  <Date dateString={date} formatString={'LLL dd'}/>
                  <span>{categories}</span>
                </small>
              </li>
            ))}
          </ul>
        </div>
      ))}
    </Layout>
  );
}

添加状态管理

首先,我们需要在归档页面中添加一个状态来跟踪当前选择的分类。我们可以使用 React 的 useState 钩子来创建一个状态变量,并使用 setSelectedCategory 函数来更新该状态。

import { useState } from 'react';

// ...

export default function Archives({ postsByYear, allPostsData }) {
  const [selectedCategory, setSelectedCategory] = useState('');
  // ...
}

处理分类按钮点击事件

接下来,我们需要处理分类按钮的点击事件,以更新选择的分类。在按钮点击时,调用 handleCategoryClick 函数并将相应的分类ID作为参数传递给它。在 handleCategoryClick 函数内部,我们使用 setSelectedCategory 函数将选定的分类ID更新到状态中。

export default function Archives({ postsByYear, allPostsData }) {
  const [selectedCategory, setSelectedCategory] = useState('');

  const handleCategoryClick = (categoryId) => {
    setSelectedCategory(categoryId);
  };

  // ...
}

筛选文章列表

现在,我们可以根据选择的分类来筛选文章列表。我们可以使用 map 函数遍历 postsByYear 数组,并使用 filter 函数对每个年份的文章列表进行筛选。如果 selectedCategory 有值,则只保留属于该分类的文章,否则保留所有文章。最后,我们可以使用 filter 函数来过滤掉没有文章的年份。

export default function Archives({ postsByYear, allPostsData }) {
  const [selectedCategory, setSelectedCategory] = useState('');

  const handleCategoryClick = (categoryId) => {
    setSelectedCategory(categoryId);
  };

  const filteredPostsByYear = postsByYear
    .map((year) => ({
      year: year.year,
      posts: year.posts.filter((post) =>
        selectedCategory ? post.categories.includes(selectedCategory) : true
      ),
    }))
    .filter((year) => year.posts.length > 0);

  // ...
}

获取所有文章分类

我们还需要获取所有文章的分类,以便在页面上显示分类按钮。我们可以使用 flatMap 方法将所有文章的分类扁平化成一个数组,并使用 Set 数据结构去重。

export default function Archives({ postsByYear, allPostsData }) {
  const [selectedCategory, setSelectedCategory] = useState('');

  const handleCategoryClick = (categoryId) => {
    setSelectedCategory(categoryId);
  };

  const filteredPostsByYear = postsByYear
    .map((year) => ({
      year: year.year,
      posts: year.posts.filter((post) =>
        selectedCategory ? post.categories.includes(selectedCategory) : true
      ),
    }))
    .filter((year) => year.posts.length > 0);

  const allCategories = Array.from(
    new Set(allPostsData.flatMap((post) => post.categories))
  );

  // ...
}

重置选择的分类

最后,我们需要添加一个重置分类的功能,使用户能够点击一个按钮来清除选择的分类,恢复显示所有文章。我们可以在按钮的点击事件中调用 handleResetCategory 函数,并使用 setSelectedCategory 将选择的分类重置为空字符串。

export default function Archives({ postsByYear, allPostsData }) {
  const [selectedCategory, setSelectedCategory] = useState('');

  const handleCategoryClick = (categoryId) => {
    setSelectedCategory(categoryId);
  };

  const filteredPostsByYear = postsByYear
    .map((year) => ({
      year: year.year,
      posts: year.posts.filter((post) =>
        selectedCategory ? post.categories.includes(selectedCategory) : true
      ),
    }))
    .filter((year) => year.posts.length > 0);

  const allCategories = Array.from(
    new Set(allPostsData.flatMap((post) => post.categories))
  );

  const handleResetCategory = () => {
    setSelectedCategory('');
  };

  // ...
}

页面展示

第一步,我们需要添加一个用于显示当前选择分类下的文章数量或总文章数量的提示文本。为此,在 <Layout> 组件中添加一个包含文本内容的 <p> 元素。

<p>
  {selectedCategory
    ? `当前分类下共有 ${allPostsData.filter((post) =>
        post.categories.includes(selectedCategory)
      ).length} 篇文章。`
    : `目前共有 ${allPostsData.length} 篇文章。`}
</p>

在上述代码中,我们首先判断是否有选择的分类,如果有则使用 filter 函数过滤出所有属于该分类的文章并返回数量;否则返回所有文章的数量。

接下来,我们添加分类按钮列表,让用户能够选择不同的分类。在 <Layout> 组件中添加如下代码:

<div>
  <button
    className={`${
      selectedCategory === '' ? 'bg-blue-600 dark:bg-blue-500 text-white' : ''
    }`}
    onClick={handleResetCategory}
  >
    全部
  </button>
  {allCategories.map((category) => (
    <button
      key={category}
      className={`${
        selectedCategory === category ? 'bg-blue-600 dark:bg-blue-500 text-white' : ''
      }`}
      onClick={() => handleCategoryClick(category)}
    >
      {category}
    </button>
  ))}
</div>

在上述代码中,我们首先添加一个“全部”按钮,用于取消当前的分类选择。当 selectedCategory 为空字符串时,该按钮将使用样式来表示当前选中状态。
接下来,我们使用 map 函数遍历 allCategories 数组中的所有分类,为每个分类添加一个按钮。对于选中的分类,我们使用样式来表示选中状态。

最后,我们按年份展示文章列表,并添加了一个过滤功能,使其只显示当前选择分类下的文章。

{filteredPostsByYear.map((year) => (
  <div key={year.year}>
    <h3>{year.year}</h3>
    <ul>
      {year.posts.map(({ id, date, title, categories }) => (
        <li key={id}>
          <Link href={`/posts/${id}`}>
            {title}
          </Link>
          <small>
            <Date dateString={date} formatString={'LLL dd'} />
            <span>{categories}</span>
          </small>
        </li>
      ))}
    </ul>
  </div>
))}

总结

通过以上步骤,我们实现了在归档页面中添加分类功能的过程。通过使用 useState 来管理选择的分类状态,处理分类按钮的点击事件,并根据选择的分类筛选文章列表,我们能够动态显示所选分类下的文章。同时,我们还添加了一个按钮来清除选择的分类,使用户能够重新查看所有文章。

请注意,这只是一个简单的实现示例,具体的实现方式可能会因项目的需求和设计而有所不同。你可以根据自己的需求进行适当的修改和定制。

然后应该有牛逼人发现这篇文章纯纯 ChatGPT 生成了,确实好用。
最近在尝试用 ChatGPT 辅助开发了,理解力很强很好用。但是出结果像玩抽卡,有时候也理解不到问题,要指点一下 ChatGPT 才能意识到问题。

the end.