Skip to content
This repository has been archived by the owner on Aug 1, 2022. It is now read-only.

Commit

Permalink
WIP: Convert classes to functions using hooks
Browse files Browse the repository at this point in the history
N.B. Does not currently work in Gatsby development mode due to
incompatibilities with react-hot-loader. See RHL issue 1088.

gaearon/react-hot-loader#1088 (comment)
  • Loading branch information
wKovacs64 committed Nov 7, 2018
1 parent 5174038 commit a5ff998
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 266 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
"lodash.startcase": "4.4.0",
"match-sorter": "2.3.0",
"prop-types": "15.6.2",
"react": "16.6.1",
"react-dom": "16.6.1",
"react": "16.7.0-alpha.0",
"react-dom": "16.7.0-alpha.0",
"react-emotion": "9.2.12",
"react-helmet": "5.2.0",
"react-icons": "3.2.2",
Expand Down
223 changes: 107 additions & 116 deletions src/components/header.js
Original file line number Diff line number Diff line change
@@ -1,148 +1,139 @@
import React, { Component, createRef } from 'react';
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'gatsby';
import { css } from 'react-emotion';
import { IconContext } from 'react-icons';
import { MdSearch, MdZoomOut } from 'react-icons/md';
import mq from '../utils/mq';

class Header extends Component {
state = {
searchTerm: '',
showSearch: false,
};
function Header({ onSearchTermChange, siteTitle, withSearch }) {
const [searchTerm, setSearchTerm] = useState('');
const [showSearch, setShowSearch] = useState(false);
const searchInput = useRef(null);

searchInput = createRef();

clearSearchTerm = () => {
this.setState({ searchTerm: '' }, () => {
this.props.onSearchTermChange('');
});
};
function clearSearchTerm() {
setSearchTerm('');
onSearchTermChange('');
}

toggleSearch = () => {
this.setState(({ showSearch }) => ({ showSearch: !showSearch }));
this.clearSearchTerm();
};
function toggleSearch() {
setShowSearch(currentShowSearch => !currentShowSearch);
clearSearchTerm();
}

handleSearchTermChange = ({ target: { value: searchTerm } }) => {
this.setState(() => ({ searchTerm }));
this.props.onSearchTermChange(searchTerm);
};
function handleSearchTermChange({ target: { value } }) {
setSearchTerm(value);
onSearchTermChange(value);
}

handleSearchTermKeydown = ({ keyCode }) => {
function handleSearchTermKeydown({ keyCode }) {
if (keyCode === 27 /* ESC */) {
this.searchInput.current.blur();
this.toggleSearch();
searchInput.current.blur();
toggleSearch();
}
};

render() {
const { siteTitle, withSearch } = this.props;
const { searchTerm, showSearch } = this.state;
}

return (
<IconContext.Provider
value={{
className: css`
vertical-align: middle;
`,
}}
return (
<IconContext.Provider
value={{
className: css`
vertical-align: middle;
`,
}}
>
<header
className={css`
background-color: #111111;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
`}
>
<header
<h1
className={css`
background-color: #111111;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
font-weight: 300;
margin: 0;
${mq.md(css`
padding: 1rem;
`)};
`}
>
<h1
<Link
to="/"
className={css`
font-weight: 300;
margin: 0;
${mq.md(css`
padding: 1rem;
`)};
color: #cccccc;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: #f4f4f4;
}
`}
>
<Link
to="/"
{siteTitle}
</Link>
</h1>
{withSearch && (
<div className="js">
<input
id="search-input"
name="searchInput"
aria-label="Search Term"
type="text"
placeholder="Search"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck={false}
disabled={!showSearch}
ref={searchInput}
value={searchTerm}
onChange={handleSearchTermChange}
onKeyDown={handleSearchTermKeydown}
className={css`
color: #f4f4f4;
background-color: transparent;
border-width: 0 0 1px;
padding: 0 0 0.25rem 0;
width: ${showSearch ? '30vw' : '0'};
max-width: 8rem;
transition: width 0.2s ease-in-out;
${mq.sm(css`
width: ${showSearch ? '10rem' : '0'};
max-width: unset;
`)};
${mq.md(css`
padding: 0 0 0.5rem 0;
width: ${showSearch ? '12rem' : '0'};
`)};
`}
/>
<button
className={css`
cursor: pointer;
color: #cccccc;
text-decoration: none;
background-color: transparent;
border: none;
transition: color 0.3s ease;
&:hover {
color: #f4f4f4;
}
${mq.md(css`
padding: 1rem;
`)};
`}
aria-label="Toggle Search"
type="button"
onClick={toggleSearch}
>
{siteTitle}
</Link>
</h1>
{withSearch && (
<div className="js">
<input
id="search-input"
name="searchInput"
aria-label="Search Term"
type="text"
placeholder="Search"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck={false}
disabled={!showSearch}
ref={this.searchInput}
value={searchTerm}
onChange={this.handleSearchTermChange}
onKeyDown={this.handleSearchTermKeydown}
className={css`
color: #f4f4f4;
background-color: transparent;
border-width: 0 0 1px;
padding: 0 0 0.25rem 0;
width: ${showSearch ? '30vw' : '0'};
max-width: 8rem;
transition: width 0.2s ease-in-out;
${mq.sm(css`
width: ${showSearch ? '10rem' : '0'};
max-width: unset;
`)};
${mq.md(css`
padding: 0 0 0.5rem 0;
width: ${showSearch ? '12rem' : '0'};
`)};
`}
/>
<button
className={css`
cursor: pointer;
color: #cccccc;
background-color: transparent;
border: none;
transition: color 0.3s ease;
&:hover {
color: #f4f4f4;
}
${mq.md(css`
padding: 1rem;
`)};
`}
aria-label="Toggle Search"
type="button"
onClick={this.toggleSearch}
>
{showSearch ? <MdZoomOut size={32} /> : <MdSearch size={32} />}
</button>
</div>
)}
</header>
</IconContext.Provider>
);
}
{showSearch ? <MdZoomOut size={32} /> : <MdSearch size={32} />}
</button>
</div>
)}
</header>
</IconContext.Provider>
);
}

Header.propTypes = {
Expand Down
92 changes: 43 additions & 49 deletions src/components/searchable-drinks-page.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { css } from 'react-emotion';
import difference from 'lodash.difference';
Expand All @@ -9,58 +9,52 @@ import mq from '../utils/mq';
import Layout from './layout';
import DrinkList from './drink-list';

class SearchableDrinksPage extends Component {
state = { searchTerm: '' };
function SearchableDrinksPage({ children, drinks }) {
const [searchTerm, setSearchTerm] = useState('');

handleSearchTermChange = searchTerm => this.setState({ searchTerm });
function handleSearchTermChange(value) {
setSearchTerm(value);
}

render() {
const { children, drinks } = this.props;
const { searchTerm } = this.state;
const containsSearchTerm = new RegExp(searchTerm, 'i');
const containsSearchTerm = new RegExp(searchTerm, 'i');

const titleMatchedDrinks = matchSorter(drinks, searchTerm, {
keys: ['title'],
});
const otherMatchedDrinks = orderBy(
difference(drinks, titleMatchedDrinks).filter(
drink =>
containsSearchTerm.test(
get(drink, 'notes.childMarkdownRemark.rawMarkdownBody'),
) || containsSearchTerm.test(drink.ingredients),
),
['rank', 'createdAt'],
['desc', 'desc'],
);
const filteredDrinks = [...titleMatchedDrinks, ...otherMatchedDrinks];
const sortedDrinks = orderBy(
drinks,
['rank', 'createdAt'],
['desc', 'desc'],
);
const titleMatchedDrinks = matchSorter(drinks, searchTerm, {
keys: ['title'],
});
const otherMatchedDrinks = orderBy(
difference(drinks, titleMatchedDrinks).filter(
drink =>
containsSearchTerm.test(
get(drink, 'notes.childMarkdownRemark.rawMarkdownBody'),
) || containsSearchTerm.test(drink.ingredients),
),
['rank', 'createdAt'],
['desc', 'desc'],
);
const filteredDrinks = [...titleMatchedDrinks, ...otherMatchedDrinks];
const sortedDrinks = orderBy(drinks, ['rank', 'createdAt'], ['desc', 'desc']);

return (
<Layout withSearch onSearchTermChange={this.handleSearchTermChange}>
{children}
{filteredDrinks.length ? (
<DrinkList drinks={searchTerm ? filteredDrinks : sortedDrinks} />
) : (
<span
className={css`
color: #eeeeee;
font-size: 1.25rem;
padding: 1rem;
${mq.sm(css`
padding: 2rem 0;
`)};
`}
>
No drinks found.
</span>
)}
</Layout>
);
}
return (
<Layout withSearch onSearchTermChange={handleSearchTermChange}>
{children}
{filteredDrinks.length ? (
<DrinkList drinks={searchTerm ? filteredDrinks : sortedDrinks} />
) : (
<span
className={css`
color: #eeeeee;
font-size: 1.25rem;
padding: 1rem;
${mq.sm(css`
padding: 2rem 0;
`)};
`}
>
No drinks found.
</span>
)}
</Layout>
);
}

SearchableDrinksPage.propTypes = {
Expand Down
Loading

0 comments on commit a5ff998

Please sign in to comment.