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

Next.js搜索组件报TypeError: Cannot read properties of undefined (reading 'toLowerCase')

问题描述

基于Next.js开发Web应用时,复制了其他可正常运行项目的搜索组件代码,但输入搜索内容时触发两个错误:

  1. TypeError: Cannot read properties of undefined (reading 'toLowerCase')
  2. 后续修改代码后又出现ReferenceError: props is not defined

相关代码如下:

/lib/posts.js

import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import remark from 'remark'
import html from 'remark-html'

const postsDirectory = path.join(process.cwd(), 'posts')

export function getSortedPostsData() {
  // Get file names under /posts
  const fileNames = fs.readdirSync(postsDirectory)
  const allPostsData = fileNames.map(fileName => {
    // Remove ".md" from file name to get id
    const id = fileName.replace(/\.md$/, '')

    // Read markdown file as string
    const fullPath = path.join(postsDirectory, fileName)
    const fileContents = fs.readFileSync(fullPath, 'utf8')

    // Use gray-matter to parse the post metadata section
    const matterResult = matter(fileContents)

    // Combine the data with the id
    return {
      id,
      ...matterResult.data
    }
  })
  // Sort posts by date
  return allPostsData.sort((a, b) => {
    if (a.date < b.date) {
      return 1
    } else {
      return -1
    }
  })
}

/pages/api/search.js

import { getSortedPostsData } from '../../lib/posts'

const posts = process.env.NODE_ENV === 'production' ? require('../../cache/data').posts : getSortedPostsData()

export default (req, res) => {
  const results = req.query.q ?
    posts.filter(post => post.title.toLowerCase().includes(req.query.q)) : []
  res.statusCode = 200
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify({ results }))
}

/components/Search.js

import { useCallback, useRef, useState } from 'react';
import Link from 'next/link';
import styles from './search.module.css';

export default function Search() {

    const searchRef = useRef(null)
    const [query, setQuery] = useState('')
    const [active, setActive] = useState(false)
    const [results, setResults] = useState([])
  
    const searchEndpoint = (query) => `/api/search?q=${query}`
  
    const onChange = useCallback((event) => {
      const query = event.target.value;
      setQuery(query)
      if (query.length) {
        fetch(searchEndpoint(query))
          .then(res => res.json())
          .then(res => {
            setResults(res.results)
          })
      } else {
        setResults([])
      }
    }, [])
  
    const onFocus = useCallback(() => {
      setActive(true)
      window.addEventListener('click', onClick)
    }, [])
  
    const onClick = useCallback((event) => {
      if (searchRef.current && !searchRef.current.contains(event.target)) {
        setActive(false)
        window.removeEventListener('click', onClick)
      }
    }, [])
  
    return (
      <div
        className={styles.container}
        ref={searchRef}
      >
        <input
          className={styles.search}
          onChange={onChange}
          onFocus={onFocus}
          placeholder='Search posts'
          type='text'
          value={query}
        />
        { active && results.length > 0 && (
          <ul className={styles.results}>
            {results.map(({ id, title }) => (
              <li className={styles.result} key={id}>
                <Link href="/posts/[id]" as={`/posts/${id}`}>
                  <a>{title}</a>
                </Link>
              </li>
            ))}
          </ul>
        ) }
      </div>
    )
  }

错误信息

error - TypeError: Cannot read properties of undefined (reading 'toLowerCase')
    at eval (webpack-internal:///(api)/./pages/api/search.js:12:67)
    at Array.filter (<anonymous>)
    at __WEBPACK_DEFAULT_EXPORT__ (webpack-internal:///(api)/./pages/api/search.js:12:41)
    at Object.apiResolver (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/api-utils/node.js:184:15)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async DevServer.runApi (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/next-server.js:403:9)
    at async Object.fn (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/base-server.js:493:37)
    at async Router.execute (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/router.js:222:36)
    at async DevServer.run (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/base-server.js:612:29)
    at async DevServer.run (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/dev/next-dev-server.js:569:20) {
  page: '/api/search'
}
   5 | export default (req, res) => {
   6 |   const results = req.query.q ?
>  7 |     posts.filter(post => post.title.toLowerCase().includes(req.query.q)) : []
     |                                    ^
   8 |   res.statusCode = 200
   9 |   res.setHeader('Content-Type', 'application/json')
  10 |   res.end(JSON.stringify({ results }))
event - compiled client and server successfully in 6.7s (581 modules)
wait  - compiling /api/search...
event - compiled successfully in 1697 ms (103 modules)
error - (api)/pages/api/search.js (7:56) @ eval
error - ReferenceError: props is not defined
    at eval (webpack-internal:///(api)/./pages/api/search.js:12:87)
    at Array.filter (<anonymous>)
    at __WEBPACK_DEFAULT_EXPORT__ (webpack-internal:///(api)/./pages/api/search.js:12:41)
    at Object.apiResolver (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/api-utils/node.js:184:15)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async DevServer.runApi (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/next-server.js:403:9)
    at async Object.fn (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/base-server.js:493:37)
    at async Router.execute (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/router.js:222:36)
    at async DevServer.run (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/base-server.js:612:29)
    at async DevServer.run (/home/konstantin/Projects/next-linearjs-docs/node_modules/next/dist/server/dev/next-dev-server.js:569:20) {
  page: '/api/search'
}
   5 | export default (req, res) => {
   6 |   const results = req.query.q ?
>  7 |     posts.filter(post => props.post.title.toLowerCase().includes(req.query.q)) : []
     |                                                        ^
   8 |   res.statusCode = 200
   9 |   res.setHeader('Content-Type', 'application/json')
  10 |   res.end(JSON.stringify({ results }))
错误原因分析

第一个错误:TypeError: Cannot read properties of undefined (reading 'toLowerCase')

这个错误指向post.title.toLowerCase(),说明你的文章中至少有一篇没有title字段。

原因在于/lib/posts.js的getSortedPostsData函数会读取posts目录下所有md文件的元数据(通过gray-matter解析),并将元数据与id合并返回。如果某篇md文件的元数据块里没写title,对应的post对象就会缺少title属性,调用toLowerCase()时自然会触发报错。另外如果生产环境用了缓存数据require('../../cache/data').posts,也可能存在缓存中部分文章缺失title的情况。

第二个错误:ReferenceError: props is not defined

这是你修改代码时的低级失误——在这个API路由函数里根本不存在props变量,错误地把post.title改成了props.post.title,属于语法错误。

解决方案

解决toLowerCase()报错

  1. 检查所有md文章的元数据:确保每篇文章的md文件开头的gray-matter块都包含title字段,示例:
---
title: "我的第一篇文章"
date: "2024-01-01"
---
  1. 添加空值保护:在搜索过滤时先判断post.title是否存在,同时把搜索关键词也转成小写,实现大小写不敏感搜索,修改/pages/api/search.js的过滤逻辑:
const results = req.query.q ?
  posts.filter(post => post.title && post.title.toLowerCase().includes(req.query.q.toLowerCase())) : []
  1. 验证缓存数据:如果生产环境使用了缓存,确保缓存生成时所有文章都包含title字段,避免缓存数据缺失属性。

解决props is not defined报错

把错误的props.post.title改回post.title即可,保持正确的变量引用。

内容的提问来源于stack exchange,提问作者Константин Водолажский

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 13:35:40