-
Players
-
- {playerOne === null
- ?
this.handleSubmit('playerOne', player)}
- />
- : this.handleReset('playerOne')}
- />
- }
-
- {playerTwo === null
- ? this.handleSubmit('playerTwo', player)}
- />
- : this.handleReset('playerTwo')}
- />
- }
-
-
-
- {playerOne && playerTwo && (
-
- Battle
-
- )}
+export default function Battle () {
+ const [playerOne, setPlayerOne] = React.useState(null)
+ const [playerTwo, setPlayerTwo] = React.useState(null)
+
+ const handleSubmit = (id, player) => id === 'playerOne'
+ ? setPlayerOne(player)
+ : setPlayerTwo(player)
+
+ const handleReset = (id) => id === 'playerOne'
+ ? setPlayerOne(null)
+ : setPlayerTwo(null)
+
+ return (
+
+
+
+
+
Players
+
+ {playerOne === null
+ ?
handleSubmit('playerOne', player)}
+ />
+ : handleReset('playerOne')}
+ />
+ }
+
+ {playerTwo === null
+ ? handleSubmit('playerTwo', player)}
+ />
+ : handleReset('playerTwo')}
+ />
+ }
-
- )
- }
+
+
+ {playerOne && playerTwo && (
+
+ Battle
+
+ )}
+
+
+ )
}
\ No newline at end of file
diff --git a/app/components/Card.js b/app/components/Card.js
index 05dcbc5..d719e5a 100644
--- a/app/components/Card.js
+++ b/app/components/Card.js
@@ -1,34 +1,32 @@
import React from 'react'
import PropTypes from 'prop-types'
-import { ThemeConsumer } from '../contexts/theme'
+import ThemeContext from '../contexts/theme'
export default function Card ({ header, subheader, avatar, href, name, children }) {
+ const theme = React.useContext(ThemeContext)
+
return (
-
- {({ theme }) => (
-
-
- {header}
-
-

- {subheader && (
-
- {subheader}
-
- )}
-
- {children}
-
+
+
+ {header}
+
+

+ {subheader && (
+
+ {subheader}
+
)}
-
+
+ {children}
+
)
}
diff --git a/app/components/Hover.js b/app/components/Hover.js
deleted file mode 100644
index de236f0..0000000
--- a/app/components/Hover.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react'
-
-export default class Hover extends React.Component {
- state = { hovering: false }
- mouseOver = () => this.setState({ hovering: true })
- mouseOut = () => this.setState({ hovering: false })
- render () {
- return (
-
- {this.props.children(this.state.hovering)}
-
- )
- }
-}
\ No newline at end of file
diff --git a/app/components/Loading.js b/app/components/Loading.js
index af58539..99f1aee 100644
--- a/app/components/Loading.js
+++ b/app/components/Loading.js
@@ -12,35 +12,29 @@ const styles = {
}
}
-export default class Loading extends React.Component {
- state = { content: this.props.text }
- componentDidMount () {
- const { speed, text } = this.props
+export default function Loading ({ text = 'Loading', speed = 300 }) {
+ const [content, setContent] = React.useState(text)
- this.interval = window.setInterval(() => {
- this.state.content === text + '...'
- ? this.setState({ content: text })
- : this.setState(({ content }) => ({ content: content + '.' }))
+ React.useEffect(() => {
+ const id = window.setInterval(() => {
+ setContent((content) => {
+ return content === `${text}...`
+ ? text
+ : `${content}.`
+ })
}, speed)
- }
- componentWillUnmount () {
- window.clearInterval(this.interval)
- }
- render() {
- return (
-
- {this.state.content}
-
- )
- }
-}
-Loading.propTypes = {
- text: PropTypes.string.isRequired,
- speed: PropTypes.number.isRequired,
+ return () => window.clearInterval(id)
+ }, [text, speed])
+
+ return (
+
+ {content}
+
+ )
}
-Loading.defaultProps = {
- text: 'Loading',
- speed: 300
+Loading.propTypes = {
+ text: PropTypes.string,
+ speed: PropTypes.number,
}
\ No newline at end of file
diff --git a/app/components/Nav.js b/app/components/Nav.js
index 26a514a..90b0cab 100644
--- a/app/components/Nav.js
+++ b/app/components/Nav.js
@@ -1,44 +1,42 @@
import React from 'react'
-import { ThemeConsumer } from '../contexts/theme'
+import ThemeContext from '../contexts/theme'
import { NavLink } from 'react-router-dom'
const activeStyle = {
color: 'rgb(187, 46, 31)'
}
-export default function Nav () {
+export default function Nav ({ toggleTheme }) {
+ const theme = React.useContext(ThemeContext)
+
return (
-
- {({ theme, toggleTheme }) => (
-
- )}
-
+
)
}
\ No newline at end of file
diff --git a/app/components/Popular.js b/app/components/Popular.js
index 76ca21f..f8b6d05 100644
--- a/app/components/Popular.js
+++ b/app/components/Popular.js
@@ -79,61 +79,56 @@ ReposGrid.propTypes = {
repos: PropTypes.array.isRequired
}
-export default class Popular extends React.Component {
- state = {
- selectedLanguage: 'All',
- repos: {},
- error: null,
- }
- componentDidMount () {
- this.updateLanguage(this.state.selectedLanguage)
- }
- updateLanguage = (selectedLanguage) => {
- this.setState({
- selectedLanguage,
+function popularReducer (state, action) {
+ if (action.type === 'success') {
+ return {
+ ...state,
+ [action.selectedLanguage]: action.repos,
error: null,
- })
+ }
+ } else if (action.type === 'error') {
+ return {
+ ...state,
+ error: action.error.message
+ }
+ } else {
+ throw new Error(`That action type isn't supported.`)
+ }
+}
+
+export default function Popular () {
+ const [selectedLanguage, setSelectedLanguage] = React.useState('All')
+ const [state, dispatch] = React.useReducer(
+ popularReducer,
+ { error: null }
+ )
+
+ const fetchedLanguages = React.useRef([])
+
+ React.useEffect(() => {
+ if (fetchedLanguages.current.includes(selectedLanguage) === false) {
+ fetchedLanguages.current.push(selectedLanguage)
- if (!this.state.repos[selectedLanguage]) {
fetchPopularRepos(selectedLanguage)
- .then((data) => {
- this.setState(({ repos }) => ({
- repos: {
- ...repos,
- [selectedLanguage]: data
- }
- }))
- })
- .catch(() => {
- console.warn('Error fetching repos: ', error)
-
- this.setState({
- error: `There was an error fetching the repositories.`
- })
- })
+ .then((repos) => dispatch({ type: 'success', selectedLanguage, repos }))
+ .catch((error) => dispatch({ type: 'error', error }))
}
- }
- isLoading = () => {
- const { selectedLanguage, repos, error } = this.state
+ }, [fetchedLanguages, selectedLanguage])
- return !repos[selectedLanguage] && error === null
- }
- render() {
- const { selectedLanguage, repos, error } = this.state
+ const isLoading = () => !state[selectedLanguage] && state.error === null
- return (
-
-
+ return (
+
+
- {this.isLoading() && }
+ {isLoading() && }
- {error && {error}
}
+ {state.error && {state.error}
}
- {repos[selectedLanguage] && }
-
- )
- }
+ {state[selectedLanguage] && }
+
+ )
}
\ No newline at end of file
diff --git a/app/components/Tooltip.js b/app/components/Tooltip.js
index a10cd55..09fce1e 100644
--- a/app/components/Tooltip.js
+++ b/app/components/Tooltip.js
@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
-import Hover from './Hover'
+import useHover from '../hooks/useHover'
const styles = {
container: {
@@ -25,15 +25,13 @@ const styles = {
}
export default function Tooltip ({ text, children }) {
+ const [hovering, attrs] = useHover()
+
return (
-
- {(hovering) => (
-
- {hovering === true &&
{text}
}
- {children}
-
- )}
-
+
+ {hovering === true &&
{text}
}
+ {children}
+
)
}
diff --git a/app/contexts/theme.js b/app/contexts/theme.js
index 7e4742b..9373368 100644
--- a/app/contexts/theme.js
+++ b/app/contexts/theme.js
@@ -1,6 +1,7 @@
import React from 'react'
-const { Consumer, Provider } = React.createContext()
+const ThemeContext = React.createContext()
-export const ThemeConsumer = Consumer
-export const ThemeProvider = Provider
\ No newline at end of file
+export default ThemeContext
+export const ThemeConsumer = ThemeContext.Consumer
+export const ThemeProvider = ThemeContext.Provider
diff --git a/app/hooks/useHover.js b/app/hooks/useHover.js
new file mode 100644
index 0000000..9a6807f
--- /dev/null
+++ b/app/hooks/useHover.js
@@ -0,0 +1,13 @@
+import React from 'react'
+
+export default function useHover () {
+ const [hovering, setHovering] = React.useState(false)
+
+ const onMouseOver = () => setHovering(true)
+ const onMouseOut = () => setHovering(false)
+
+ return [hovering, {
+ onMouseOut,
+ onMouseOver
+ }]
+}
\ No newline at end of file
diff --git a/app/index.js b/app/index.js
index 33a7308..09766cd 100644
--- a/app/index.js
+++ b/app/index.js
@@ -10,37 +10,30 @@ const Popular = React.lazy(() => import('./components/Popular'))
const Battle = React.lazy(() => import('./components/Battle'))
const Results = React.lazy(() => import('./components/Results'))
-class App extends React.Component {
- state = {
- theme: 'light',
- toggleTheme: () => {
- this.setState(({ theme }) => ({
- theme: theme === 'light' ? 'dark' : 'light'
- }))
- }
- }
- render() {
- return (
-
-
-
-
-
+function App () {
+ const [theme, setTheme] = React.useState('light')
+ const toggleTheme = () => setTheme((theme) => theme === 'light' ? 'dark' : 'light')
- } >
-
-
-
-
- 404
} />
-
-
-
+ return (
+
+
+
+
+
+
+ } >
+
+
+
+
+ 404
} />
+
+
-
-
- )
- }
+
+
+
+ )
}
ReactDOM.render(