首页 > 解决方案 > 将导航状态从反应组件传递到页面时,盖茨比构建失败

问题描述

我正在使用 Gatsby 构建一个帮助页面,并有一个搜索栏 ( Searchbar.js ),我试图在其中传递用户在该字段中的输入(搜索栏始终存在于页面中——就像Evernote 的帮助页面一样)执行搜索的组件 ( search.js ),然后将该输出传递到实际结果页面 ( SearchResults.js )。

当我做gatsby develop一切工作时,gatsby build我得到一个错误,它说它无法读取属性“查询”,因为它未定义(search.js: 的第 63 行var search = location.state.query.trim())。为什么构建失败?

搜索栏.js

import React from 'react'
import { navigate } from 'gatsby'
import { FaSearch } from 'react-icons/fa'
import searchbarStyles from "./searchbar.module.css"

export default class Search extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            search: ''
        }

        this.handleSubmit = this.handleSubmit.bind(this)
        this.handleChange = this.handleChange.bind(this)
    }

    handleSubmit(event) {
        event.preventDefault()
        var query = this.state.search
        navigate(
            "/search/",
            {
                state: { query },
            }
        )
    }

    handleChange(event) {
        this.setState({
            search: event.target.value
        })
    }

    render(){
        return (
            <div className={searchbarStyles.global_search}>
                <div className={searchbarStyles.row}>
                    <form className={searchbarStyles.search} onSubmit={this.handleSubmit}>
                        <input
                            type='text'
                            id='globalSearchInput'
                            className=''
                            placeholder='Search Help & Training'
                            autoComplete='off'
                            value={this.state.search}
                            onChange={this.handleChange}
                        />
                        <button
                            type='submit'
                            disabled={!this.state.search}
                        ><FaSearch className={searchbarStyles.searchIcon}/></button>
                    </form>
                </div>
            </div>
        )
    }
}

搜索.js

import React, { useMemo } from 'react'
import Layout from '../components/Layout'
import SearchResults from '../components/SearchResults'
import { graphql } from 'gatsby'
import Fuse from 'fuse.js'

const matchThreshold = .65

//options for the fuzzy search
var fuseOptions = {
  shouldSort: true,
  threshold: matchThreshold,
  location: 0,
  distance: 99999999999,
  minMatchCharLength: 1,
  includeMatches: true,
  includeScore: true,
  keys: [
    {name: "title", weight: 0.3 },
    {name: "content", weight: 0.7}
  ]
};


function cleanString(string) {
  const re = /(&nbsp;|<([^>]+)>)/ig
  return string.replace(re,'') 
}

function FuzzySearch (query, data) {
  fuseOptions.minMatchCharLength = query.length
  var dataPrepped = data.map(function(element) {
    return {
      "title": element.node.frontmatter.title,
      "content": cleanString(element.node.html),
      "slug": element.node.fields.slug,
    }
  })
  var fuse = useMemo(() =>  new Fuse(dataPrepped, fuseOptions), [])
  var results = fuse.search(query)

  //customize the results to only return matches within desired threshold
  return results.filter(function(match) {
    if(match.score <= matchThreshold) {
      return true
    }
    return false
  }).map(function(match) {
    return {
      "title": match.item.title,
      "slug": match.item.slug,
      "matches": match.matches
    }
  })
}



export default ({ location, data }) => {
  console.log("SERACH.JS\n")
  console.log(JSON.stringify(location))
  var search = location.state.query.trim()
  var results = []
  if(search.length) results = FuzzySearch(search, data.allMarkdownRemark.edges)

  return (
    <Layout>
      <SearchResults FoundItems={results} SearchedTerm={search}>  </SearchResults>      
    </Layout>
  )

}

export const query = graphql `
query MyQuery {
  allMarkdownRemark {
    edges {
      node {
        fields {
          slug
        }       
        frontmatter {
          title
          date
          doctype
        }
        html
      }
    }
  }
}
`

搜索结果.js

import React from 'react'
import { Link } from 'gatsby'
import searchResultStyles from "./searchresults.module.css"


function resultsPage(resultsBlurb, results) {
  return(
    <div className={searchResultStyles.content}>
      <h1>Search Results</h1>
      <p className={searchResultStyles.resultBlurb}>{resultsBlurb}</p>
      <ol>
        {results.map((match) => (
          <li>
            <div className={searchResultStyles.resultContent}>
              <Link to={match.slug} className={searchResultStyles.resultTitle}>{match.title}</Link>
              {match.matches.map(function(instanceOfMatch) {
                if(instanceOfMatch.key === "content") {
                  let startIndex = instanceOfMatch.indices[0][0]
                  return(
                    <p className={searchResultStyles.resultExcerpt}>{`...${instanceOfMatch.value.substring(startIndex, startIndex + 100)}...`}</p>
                  )
                }
              })}
              </div>
          </li>
        ))}
      </ol>
    </div>
  )
}
class SearchResults extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      results: props.FoundItems,
      term: props.SearchedTerm,

    }

    this.updateBlurb = this.updateBlurb.bind(this)
  }

  updateBlurb() {
    let resultsBlurb = `Sorry--could not find any results for "${this.state.term}"`

    if (this.state.results.length) {
      resultsBlurb = (this.state.results.length === 1) ? `Only 1 item found for "${this.state.term}"` : `Found ${this.state.results.length} items for "${this.state.term}"`
    }

    return resultsBlurb
  }

  componentDidUpdate(prevProps) {
    if(prevProps!== this.props) {
      this.setState ({
        results: this.props.FoundItems,
        term: this.props.SearchedTerm
      })
    }

    return(resultsPage(this.updateBlurb(), this.props.FoundItems))
  }

  render() {
    return(resultsPage(this.updateBlurb(), this.state.results))
  }
}

export default SearchResults

解决方案 (内search.js


export default ({ location, data }) => {
  var search = ''
  var results = []
  if(typeof window !== "undefiend") search = location.state.query.trim()
  if(search.length) results = ...

标签: javascriptreactjsreact-routerreact-hooksgatsby

解决方案


location是 . 的缩写window.location,但在构建时,您的代码在没有window. 而是考虑在运行调用之前测试window( ) 的存在,并在不存在的情况下回退到默认值。typeof window !== "undefined"location.state.query.trimwindow


推荐阅读