Refactor class components to functional @ examples (#13398)

**Affected examples**

with-segment-analytics
with-slate
with-portals-ssr ( _with-portals-ssr is based on a package not being updated in the last year_)
with-videojs
with-next-page-transitions
with-firebase-cloud-messaging
with-dynamic-app-layout
with-dynamic-import
with-next-transition
with-carbon-components
with-cerebral
with-custom-babel-config

Here and there I have removed some redundant imports as well. I believe with this PR, there are only 1 or 2 examples left using class-based components. If by any chance you find any, let me know and I'll refactor them too.

If you don't like anything or you want me to change something, please let me know.

**If there is anything else you'd like me to help with, I would be honored to assist.**
This commit is contained in:
TodorTotev 2020-05-27 08:14:26 +03:00 committed by GitHub
parent dcf446d83d
commit 4a02dc5a17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 247 additions and 301 deletions

View file

@ -1,12 +1,11 @@
import { Component, Fragment } from 'react'
import { Button } from 'carbon-components-react' import { Button } from 'carbon-components-react'
export default class DemoApp extends Component { const Home = () => {
render() { return (
return ( <>
<Fragment> <Button>Hello, world!</Button>
<Button>Hello, world!</Button> </>
</Fragment> )
)
}
} }
export default Home

View file

@ -1,29 +1,26 @@
import { Component } from 'react'
import { Controller } from 'cerebral' import { Controller } from 'cerebral'
import Devtools from 'cerebral/devtools' import Devtools from 'cerebral/devtools'
import { Container } from '@cerebral/react' import { Container } from '@cerebral/react'
import Page from '../components/Page' import Page from '../components/Page'
import clock from '../modules/clock' import clock from '../modules/clock'
export default class Counter extends Component { const Home = (props) => {
constructor(props) { const { stateChanges } = props
super(props)
// The controller will be instantiated for every page change and we only const controller = Controller({
// add the devtools if we indeed are running in the browser devtools:
this.controller = Controller({ process.env.NODE_ENV === 'production' || typeof window === 'undefined'
devtools: ? null
process.env.NODE_ENV === 'production' || typeof window === 'undefined' : Devtools({ host: 'localhost:8787' }),
? null modules: { clock },
: Devtools({ host: 'localhost:8787' }), stateChanges: stateChanges,
modules: { clock }, })
stateChanges: props.stateChanges,
}) return (
} <Container controller={controller}>
render() { <Page title="Index Page" linkTo="/other" />
return ( </Container>
<Container controller={this.controller}> )
<Page title="Index Page" linkTo="/other" />
</Container>
)
}
} }
export default Home

View file

@ -1,29 +1,26 @@
import { Component } from 'react'
import { Controller } from 'cerebral' import { Controller } from 'cerebral'
import Devtools from 'cerebral/devtools' import Devtools from 'cerebral/devtools'
import { Container } from '@cerebral/react' import { Container } from '@cerebral/react'
import Page from '../components/Page' import Page from '../components/Page'
import clock from '../modules/clock' import clock from '../modules/clock'
export default class Counter extends Component { const Other = (props) => {
constructor(props) { const { stateChanges } = props
super(props)
// The controller will be instantiated for every page change and we only const controller = Controller({
// add the devtools if we indeed are running in the browser devtools:
this.controller = Controller({ process.env.NODE_ENV === 'production' || typeof window === 'undefined'
devtools: ? null
process.env.NODE_ENV === 'production' || typeof window === 'undefined' : Devtools({ host: 'localhost:8787' }),
? null modules: { clock },
: Devtools({ host: 'localhost:8787' }), stateChanges: stateChanges,
modules: { clock }, })
stateChanges: props.stateChanges,
}) return (
} <Container controller={controller}>
render() { <Page title="Index Page" linkTo="/other" />
return ( </Container>
<Container controller={this.controller}> )
<Page title="Other Page" linkTo="/" />
</Container>
)
}
} }
export default Other

View file

@ -1,48 +1,37 @@
import { Component } from 'react' import { useState, useEffect } from 'react'
export default class MyLuckNo extends Component {
constructor(...args) { const MyLuckNo = () => {
super(...args) const [randomNumber, setRandomNumber] = useState(null)
this.state = { randomNo: null }
const recalculate = () => {
setRandomNumber(Math.ceil(Math.random() * 100))
} }
componentDidMount() { useEffect(() => {
this.recalculate() recalculate()
} }, [])
recalculate() { const message = do {
this.setState({ if (randomNumber < 30) {
randomNo: Math.ceil(Math.random() * 100), // eslint-disable-next-line no-unused-expressions
}) ;('Do not give up. Try again.')
} } else if (randomNumber < 60) {
// eslint-disable-next-line no-unused-expressions
render() { ;('You are a lucky guy')
const { randomNo } = this.state } else {
// eslint-disable-next-line no-unused-expressions
if (randomNo === null) { ;('You are soooo lucky!')
return <p>Please wait..</p>
} }
// This is an experimental JavaScript feature where we can get with
// using babel-preset-stage-0
const message = do {
if (randomNo < 30) {
// eslint-disable-next-line no-unused-expressions
;('Do not give up. Try again.')
} else if (randomNo < 60) {
// eslint-disable-next-line no-unused-expressions
;('You are a lucky guy')
} else {
// eslint-disable-next-line no-unused-expressions
;('You are soooo lucky!')
}
}
return (
<div>
<h3>Your Lucky number is: "{randomNo}"</h3>
<p>{message}</p>
<button onClick={() => this.recalculate()}>Try Again</button>
</div>
)
} }
if (randomNumber === null) return <p>Please wait..</p>
return (
<div>
<h3>Your Lucky number is: "{randomNumber}"</h3>
<p>{message}</p>
<button onClick={() => recalculate()}>Try Again</button>
</div>
)
} }
export default MyLuckNo

View file

@ -1,4 +1,3 @@
import React from 'react'
import App from 'next/app' import App from 'next/app'
const Noop = ({ children }) => children const Noop = ({ children }) => children

View file

@ -1,13 +1,12 @@
import { Component } from 'react' import { useEffect } from 'react'
import { firebaseCloudMessaging } from '../utils/webPush' import { firebaseCloudMessaging } from '../utils/webPush'
class Index extends Component { const Index = () => {
componentDidMount() { useEffect(() => {
firebaseCloudMessaging.init() firebaseCloudMessaging.init()
} }, [])
render() {
return <div>Next.js with firebase cloud messaging.</div> return <div>Next.js with firebase cloud messaging.</div>
}
} }
export default Index export default Index

View file

@ -1,43 +1,35 @@
import React from 'react' import { useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Link from 'next/link' import Link from 'next/link'
class About extends React.Component { const About = (props) => {
static pageTransitionDelayEnter = true const [loaded, setLoaded] = useState(false)
const { pageTransitionReadyToEnter } = props
constructor(props) { useEffect(() => {
super(props) const timeoutId = setTimeout(() => {
this.state = { pageTransitionReadyToEnter()
loaded: false, setLoaded(true)
}
}
componentDidMount() {
this.timeoutId = setTimeout(() => {
this.props.pageTransitionReadyToEnter()
this.setState({ loaded: true })
}, 2000) }, 2000)
} return () => {
clearTimeout(timeoutId)
}
}, [pageTransitionReadyToEnter])
componentWillUnmount() { if (!loaded) return null
if (this.timeoutId) clearTimeout(this.timeoutId)
}
render() { return (
if (!this.state.loaded) return null <div className="container bg-success page">
return ( <h1>About us</h1>
<div className="container bg-success page"> <p>
<h1>About us</h1> Notice how a loading spinner showed up while my content was "loading"?
<p> Pretty neat, huh?
Notice how a loading spinner showed up while my content was "loading"? </p>
Pretty neat, huh? <Link href="/">
</p> <a className="btn btn-light">Go back home</a>
<Link href="/"> </Link>
<a className="btn btn-light">Go back home</a> </div>
</Link> )
</div>
)
}
} }
About.propTypes = { About.propTypes = {

View file

@ -7,7 +7,7 @@
"dependencies": { "dependencies": {
"@jesstelford/react-portal-universal": "1.0.0", "@jesstelford/react-portal-universal": "1.0.0",
"next": "latest", "next": "latest",
"react": "^16.8.0", "react": "16.13.1",
"react-dom": "^16.8.0" "react-dom": "16.13.1"
} }
} }

View file

@ -1,71 +1,60 @@
import { Component } from 'react' import { useState } from 'react'
import { UniversalPortal } from '@jesstelford/react-portal-universal' import { UniversalPortal } from '@jesstelford/react-portal-universal'
export default class Index extends Component { const Index = () => {
constructor() { const [isOpen, toggle] = useState(true)
super(...arguments)
this.state = { opened: true }
}
open = () => { return (
this.setState({ opened: true }) <>
} {/* A portal that is adjacent to its target */}
<div id="target" />
<UniversalPortal selector="#target">
<h1>Hello Portal</h1>
</UniversalPortal>
close = () => { {/* Open a modal in a portal that is elsewhere in the react tree */}
this.setState({ opened: false }) <button onClick={() => toggle(!isOpen)} type="button">
} Open Modal
</button>
render() { {isOpen && (
return ( <UniversalPortal selector="#modal">
<> <div
{/* A portal that is adjacent to its target */} style={{
<div id="target" /> position: 'fixed',
<UniversalPortal selector="#target"> backgroundColor: 'rgba(0, 0, 0, 0.7)',
<h1>Hello Portal</h1> top: 0,
</UniversalPortal> right: 0,
bottom: 0,
{/* Open a modal in a portal that is elsewhere in the react tree */} left: 0,
<button type="button" onClick={this.open}> }}
Open Modal >
</button>
{this.state.opened && (
<UniversalPortal selector="#modal">
<div <div
style={{ style={{
position: 'fixed', backgroundColor: 'white',
backgroundColor: 'rgba(0, 0, 0, 0.7)', position: 'absolute',
top: 0, top: '10%',
right: 0, right: '10%',
bottom: 0, bottom: '10%',
left: 0, left: '10%',
padding: '1em',
}} }}
> >
<div <p>
style={{ This modal is rendered using{' '}
backgroundColor: 'white', <a href="https://www.npmjs.com/package/@jesstelford/react-portal-universal">
position: 'absolute', <code>@jesstelford/react-portal-universal</code>
top: '10%', </a>
right: '10%', .
bottom: '10%', </p>
left: '10%', <button type="button" onClick={() => toggle(!isOpen)}>
padding: '1em', Close Modal
}} </button>
>
<p>
This modal is rendered using{' '}
<a href="https://www.npmjs.com/package/@jesstelford/react-portal-universal">
<code>@jesstelford/react-portal-universal</code>
</a>
.
</p>
<button type="button" onClick={this.close}>
Close Modal
</button>
</div>
</div> </div>
</UniversalPortal> </div>
)} </UniversalPortal>
</> )}
) </>
} )
} }
export default Index

View file

@ -8,7 +8,7 @@
"dependencies": { "dependencies": {
"@segment/snippet": "^4.0.1", "@segment/snippet": "^4.0.1",
"next": "latest", "next": "latest",
"react": "^16.7.0", "react": "16.13.1",
"react-dom": "^16.7.0" "react-dom": "16.13.1"
} }
} }

View file

@ -1,49 +1,47 @@
import { Component } from 'react' import { useState } from 'react'
export default class Contact extends Component { const Contact = () => {
state = { message: '' } const [message, setMessage] = useState('')
render() { const handleSubmit = (e) => {
return (
<div>
<h1>This is the Contact page</h1>
<form onSubmit={this.handleSubmit}>
<label>
<span>Message:</span>
<textarea onChange={this.handleInput} value={this.state.message} />
</label>
<button type="submit">submit</button>
</form>
<style jsx>{`
label span {
display: block;
margin-bottom: 12px;
}
textarea {
min-width: 300px;
min-height: 120px;
}
button {
margin-top: 12px;
display: block;
}
`}</style>
</div>
)
}
handleInput = (e) => {
this.setState({ message: e.target.value })
}
handleSubmit = (e) => {
e.preventDefault() e.preventDefault()
global.analytics.track('Form Submitted', { global.analytics.track('Form Submitted', {
message: this.state.message, message,
}) })
this.setState({ message: '' }) setMessage('')
} }
return (
<div>
<h1>This is the Contact page</h1>
<form onSubmit={handleSubmit}>
<label>
<span>Message:</span>
<textarea
onChange={(e) => setMessage(e.target.value)}
value={message}
/>
</label>
<button type="submit">submit</button>
</form>
<style jsx>{`
label span {
display: block;
margin-bottom: 12px;
}
textarea {
min-width: 300px;
min-height: 120px;
}
button {
margin-top: 12px;
display: block;
}
`}</style>
</div>
)
} }
export default Contact

View file

@ -1,21 +1,18 @@
import { Component } from 'react'
import Link from 'next/link' import Link from 'next/link'
import Editor from '../components/NextEditor' import Editor from '../components/NextEditor'
class MultipleEditors extends Component { const MultipleEditors = () => {
render() { return (
return ( <>
<> <Link href="/">
<Link href="/"> <a>Go to Home</a>
<a>Go to Home</a> </Link>
</Link> <hr />
<hr /> <Editor slateKey="foo" defaultValue="Foo" />
<Editor slateKey="foo" defaultValue="Foo" /> <hr />
<hr /> <Editor slateKey="bar" defaultValue="Bar" />
<Editor slateKey="bar" defaultValue="Bar" /> </>
</> )
)
}
} }
export default MultipleEditors export default MultipleEditors

View file

@ -1,34 +1,25 @@
import { Component } from 'react' import { useCallback, useEffect, useState } from 'react'
import videojs from 'video.js' import videojs from 'video.js'
import 'videojs-youtube' import 'videojs-youtube'
class Player extends Component { const Player = (props) => {
componentDidMount() { const [videoEl, setVideoEl] = useState(null)
// instantiate Video.js const onVideo = useCallback((el) => {
this.player = videojs(this.videoNode, this.props, function onPlayerReady() { setVideoEl(el)
console.log('onPlayerReady', this) }, [])
})
}
// destroy player on unmount useEffect(() => {
componentWillUnmount() { const player = videojs(videoEl, props)
if (this.player) { return () => {
this.player.dispose() player.dispose()
} }
} }, [props, videoEl])
// wrap the player in a div with a `data-vjs-player` attribute return (
// so videojs won't create additional wrapper in the DOM <div data-vjs-player>
// see https://github.com/videojs/video.js/pull/3856 <video ref={onVideo} className="video-js" playsInline />
render() { </div>
return ( )
<div>
<div data-vjs-player>
<video ref={(node) => (this.videoNode = node)} className="video-js" />
</div>
</div>
)
}
} }
export default Player export default Player

View file

@ -8,8 +8,8 @@
}, },
"dependencies": { "dependencies": {
"next": "^9.1.8-canary.11", "next": "^9.1.8-canary.11",
"react": "^16.7.0", "react": "16.13.1",
"react-dom": "^16.7.0", "react-dom": "16.13.1",
"video.js": "^6.7.3", "video.js": "^6.7.3",
"videojs-youtube": "^2.4.1" "videojs-youtube": "^2.4.1"
}, },

View file

@ -1,20 +1,19 @@
import { Component } from 'react'
import Player from '../components/Player' import Player from '../components/Player'
export default class Index extends Component { const Index = () => {
render() { const videoJsOptions = {
const videoJsOptions = { techOrder: ['youtube'],
techOrder: ['youtube'], autoplay: false,
autoplay: false, controls: true,
controls: true, sources: [
sources: [ {
{ src: 'https://www.youtube.com/watch?v=IxQB14xVas0',
src: 'https://www.youtube.com/watch?v=IxQB14xVas0', type: 'video/youtube',
type: 'video/youtube', },
}, ],
],
}
return <Player {...videoJsOptions} />
} }
return <Player {...videoJsOptions} />
} }
export default Index