mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
docs: add algolia-react-instantsearch example (#2544)
* docs: add algolia-react-instantsearch example * fix: style
This commit is contained in:
parent
24c2e9954a
commit
94c484e80d
19
examples/with-algolia-react-instantsearch/.gitignore
vendored
Normal file
19
examples/with-algolia-react-instantsearch/.gitignore
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
/dist
|
||||
/.next
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
34
examples/with-algolia-react-instantsearch/README.md
Normal file
34
examples/with-algolia-react-instantsearch/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-algolia-react-instantsearch)
|
||||
|
||||
# With Algolia React InstantSearch example
|
||||
|
||||
## How to use
|
||||
|
||||
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||
|
||||
```bash
|
||||
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-algolia-react-instantsearch
|
||||
cd with-algolia-react-instantsearch
|
||||
```
|
||||
|
||||
Set up Algolia:
|
||||
- create an [algolia](https://www.algolia.com/) account or use this already [configured index](https://community.algolia.com/react-instantsearch/Getting_started.html#before-we-start)
|
||||
- update the appId, apikey and indexName you want to search on in components/app.js
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))
|
||||
|
||||
```bash
|
||||
now
|
||||
```
|
||||
|
||||
## The idea behind the example
|
||||
The goal of this example is to illustrate how you can use [Algolia React InstantSearch](https://community.algolia.com/react-instantsearch/) to perform
|
||||
your search with a Server-rendered application developed with Next.js. It also illustrates how you
|
||||
can keep in sync the Url with the search.
|
86
examples/with-algolia-react-instantsearch/components/app.js
Normal file
86
examples/with-algolia-react-instantsearch/components/app.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
RefinementList,
|
||||
SearchBox,
|
||||
Hits,
|
||||
Configure,
|
||||
Highlight,
|
||||
Pagination
|
||||
} from 'react-instantsearch/dom'
|
||||
import { InstantSearch } from './instantsearch'
|
||||
|
||||
const HitComponent = ({ hit }) =>
|
||||
<div className='hit'>
|
||||
<div>
|
||||
<div className='hit-picture'>
|
||||
<img src={`${hit.image}`} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='hit-content'>
|
||||
<div>
|
||||
<Highlight attributeName='name' hit={hit} />
|
||||
<span>
|
||||
{' '}- ${hit.price}
|
||||
</span>
|
||||
<span>
|
||||
{' '}- {hit.rating} stars
|
||||
</span>
|
||||
</div>
|
||||
<div className='hit-type'>
|
||||
<Highlight attributeName='type' hit={hit} />
|
||||
</div>
|
||||
<div className='hit-description'>
|
||||
<Highlight attributeName='description' hit={hit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
HitComponent.propTypes = {
|
||||
hit: PropTypes.object
|
||||
}
|
||||
|
||||
export default class extends React.Component {
|
||||
static propTypes = {
|
||||
searchState: PropTypes.object,
|
||||
resultsState: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
onSearchStateChange: PropTypes.func
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<InstantSearch
|
||||
appId='appId' // change this
|
||||
apiKey='apiKey' // change this
|
||||
indexName='indexName' // change this
|
||||
resultsState={this.props.resultsState}
|
||||
onSearchStateChange={this.props.onSearchStateChange}
|
||||
searchState={this.props.searchState}
|
||||
>
|
||||
<Configure hitsPerPage={10} />
|
||||
<header>
|
||||
<h1>React InstantSearch + Next.Js</h1>
|
||||
<SearchBox />
|
||||
</header>
|
||||
<content>
|
||||
<menu>
|
||||
<RefinementList attributeName='category' />
|
||||
</menu>
|
||||
<results>
|
||||
<Hits hitComponent={HitComponent} />
|
||||
</results>
|
||||
</content>
|
||||
<footer>
|
||||
<Pagination />
|
||||
<div>
|
||||
See{' '}
|
||||
<a href='https://github.com/algolia/react-instantsearch/tree/master/packages/react-instantsearch/examples/next-app'>
|
||||
source code
|
||||
</a>{' '}
|
||||
on github
|
||||
</div>
|
||||
</footer>
|
||||
</InstantSearch>
|
||||
)
|
||||
}
|
||||
}
|
48
examples/with-algolia-react-instantsearch/components/head.js
Normal file
48
examples/with-algolia-react-instantsearch/components/head.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import NextHead from 'next/head'
|
||||
import { string } from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const defaultDescription = ''
|
||||
const defaultOGURL = ''
|
||||
const defaultOGImage = ''
|
||||
|
||||
export const Head = props =>
|
||||
<NextHead>
|
||||
<meta charSet='UTF-8' />
|
||||
<title>{props.title || ''}</title>
|
||||
<meta
|
||||
name='description'
|
||||
content={props.description || defaultDescription}
|
||||
/>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<link rel='icon' sizes='192x192' href='/static/touch-icon.png' />
|
||||
<link rel='apple-touch-icon' href='/static/touch-icon.png' />
|
||||
<link rel='mask-icon' href='/static/favicon-mask.svg' color='#49B882' />
|
||||
<link rel='icon' href='/static/favicon.ico' />
|
||||
<meta property='og:url' content={props.url || defaultOGURL} />
|
||||
<meta property='og:title' content={props.title || ''} />
|
||||
<meta
|
||||
property='og:description'
|
||||
content={props.description || defaultDescription}
|
||||
/>
|
||||
<meta name='twitter:site' content={props.url || defaultOGURL} />
|
||||
<meta name='twitter:card' content='summary_large_image' />
|
||||
<meta name='twitter:image' content={props.ogImage || defaultOGImage} />
|
||||
<meta property='og:image' content={props.ogImage || defaultOGImage} />
|
||||
<meta property='og:image:width' content='1200' />
|
||||
<meta property='og:image:height' content='630' />
|
||||
<link
|
||||
rel='stylesheet'
|
||||
href='https://unpkg.com/react-instantsearch-theme-algolia@3.0.0/style.min.css'
|
||||
/>
|
||||
<link rel='stylesheet' href='../static/instantsearch.css' />
|
||||
</NextHead>
|
||||
|
||||
Head.propTypes = {
|
||||
title: string,
|
||||
description: string,
|
||||
url: string,
|
||||
ogImage: string
|
||||
}
|
||||
|
||||
export default Head
|
|
@ -0,0 +1,3 @@
|
|||
export * from './head'
|
||||
export { default as App } from './app'
|
||||
export { findResultsState } from './instantsearch'
|
|
@ -0,0 +1,5 @@
|
|||
import { createInstantSearch } from 'react-instantsearch/server'
|
||||
|
||||
const { InstantSearch, findResultsState } = createInstantSearch()
|
||||
|
||||
export { InstantSearch, findResultsState }
|
19
examples/with-algolia-react-instantsearch/next.config.js
Normal file
19
examples/with-algolia-react-instantsearch/next.config.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* eslint-disable import/no-commonjs */
|
||||
|
||||
module.exports = {
|
||||
webpack: config => {
|
||||
// Fixes npm packages that depend on `fs` module
|
||||
config.node = {
|
||||
fs: 'empty'
|
||||
}
|
||||
config.module.rules.push({
|
||||
test: /\.css$/,
|
||||
loader: ['style-loader', 'css-loader']
|
||||
})
|
||||
if (config.resolve.alias) {
|
||||
delete config.resolve.alias.react
|
||||
delete config.resolve.alias['react-dom']
|
||||
}
|
||||
return config
|
||||
}
|
||||
}
|
19
examples/with-algolia-react-instantsearch/package.json
Normal file
19
examples/with-algolia-react-instantsearch/package.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "create-next-example-app",
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "NODE_ENV=development next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"css-loader": "^0.28.1",
|
||||
"next": "^2.4.7",
|
||||
"prop-types": "^15.5.10",
|
||||
"qs": "^6.4.0",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-instantsearch": "beta",
|
||||
"react-instantsearch-theme-algolia": "beta",
|
||||
"style-loader": "^0.17.0"
|
||||
}
|
||||
}
|
73
examples/with-algolia-react-instantsearch/pages/index.js
Normal file
73
examples/with-algolia-react-instantsearch/pages/index.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { Head, App, findResultsState } from '../components'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Router from 'next/router'
|
||||
import qs from 'qs'
|
||||
|
||||
const updateAfter = 700
|
||||
|
||||
const searchStateToUrl = searchState =>
|
||||
searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : ''
|
||||
|
||||
export default class extends React.Component {
|
||||
static propTypes = {
|
||||
resultsState: PropTypes.object,
|
||||
searchState: PropTypes.object
|
||||
};
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.onSearchStateChange = this.onSearchStateChange.bind(this)
|
||||
}
|
||||
|
||||
/*
|
||||
nextjs params.query doesn't handle nested objects
|
||||
once it does, params.query could be used directly here, but also inside the constructor
|
||||
to initialize the searchState.
|
||||
*/
|
||||
static async getInitialProps (params) {
|
||||
const searchState = qs.parse(
|
||||
params.asPath.substring(params.asPath.indexOf('?') + 1)
|
||||
)
|
||||
const resultsState = await findResultsState(App, { searchState })
|
||||
return { resultsState, searchState }
|
||||
}
|
||||
|
||||
onSearchStateChange = searchState => {
|
||||
clearTimeout(this.debouncedSetState)
|
||||
this.debouncedSetState = setTimeout(() => {
|
||||
const href = searchStateToUrl(searchState)
|
||||
Router.push(href, href, {
|
||||
shallow: true
|
||||
})
|
||||
}, updateAfter)
|
||||
this.setState({ searchState })
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.setState({ searchState: qs.parse(window.location.search.slice(1)) })
|
||||
}
|
||||
|
||||
componentWillReceiveProps () {
|
||||
this.setState({ searchState: qs.parse(window.location.search.slice(1)) })
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<Head title='Home' />
|
||||
<div>
|
||||
<App
|
||||
resultsState={this.props.resultsState}
|
||||
onSearchStateChange={this.onSearchStateChange}
|
||||
searchState={
|
||||
this.state && this.state.searchState
|
||||
? this.state.searchState
|
||||
: this.props.searchState
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
BIN
examples/with-algolia-react-instantsearch/static/favicon.ico
Normal file
BIN
examples/with-algolia-react-instantsearch/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,48 @@
|
|||
.ais-InstantSearch__root {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
menu {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
results {
|
||||
flex: 10;
|
||||
}
|
||||
|
||||
.hit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hit-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hit-content {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.hit-picture img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.hit-type {
|
||||
color: #888888;
|
||||
font-size: 13px;
|
||||
}
|
Loading…
Reference in a new issue