Сбой сборки gatsby при передаче состояния навигации от реагирующего компонента на страницу - PullRequest
0 голосов
/ 28 апреля 2020

Я создаю страницу справки с помощью Gatsby и у меня есть панель поиска ( Панель поиска. js), где я пытаюсь передать введенные пользователем данные в поле ( панель поиска всегда присутствует на странице - представьте, как страница справки Evernote ) для компонента, который осуществляет поиск ( search. js), который затем передает этот вывод на страницу фактических результатов ( SearchResults. js).

Когда я делаю gatsby develop, все работает как надо, но когда я делаю gatsby build, я получаю сообщение об ошибке, в котором говорится, что он не может прочитать свойство "query", поскольку оно не определено (строка 63 поиска. js: 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>
        )
    }
}

search. 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
      }
    }
  }
}
`

SearchResults. 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 = ...

1 Ответ

0 голосов
/ 28 апреля 2020

location - это сокращение от window.location, но во время сборки ваш код выполняется в Node.js, который не имеет window. Вместо этого рассмотрите возможность проверки существования window (typeof window !== "undefined") перед выполнением вызова location.state.query.trim и вернитесь к значению по умолчанию в случае, если window не существует.

...