Last active
December 5, 2021 19:20
-
-
Save webuniverseio/3404381b5b9b6d99c3d74b9a5cc6c075 to your computer and use it in GitHub Desktop.
reading order
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://epicreact.dev/why-you-shouldnt-put-refs-in-a-dependency-array/ | |
import * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
function UsernameForm({ | |
initialUsername = '', | |
onSubmitUsername, | |
}) { | |
const [username, setUsername] = React.useState(initialUsername) | |
const [touched, setTouched] = React.useState(false) | |
const usernameInputRef = React.useRef(null) | |
const usernameIsLowerCase = username === username.toLowerCase() | |
const usernameIsLongEnough = username.length >= 3 | |
const usernameIsShortEnough = username.length <= 10 | |
const formIsValid = | |
usernameIsShortEnough && usernameIsLongEnough && usernameIsLowerCase | |
const displayErrorMessage = touched && !formIsValid | |
React.useEffect(() => { | |
if (displayErrorMessage) usernameInputRef.current?.focus() | |
}, [displayErrorMessage]) | |
let errorMessage = null | |
if (!usernameIsLowerCase) { | |
errorMessage = 'Username must be lower case' | |
} else if (!usernameIsLongEnough) { | |
errorMessage = 'Username must be at least 3 characters long' | |
} else if (!usernameIsShortEnough) { | |
errorMessage = 'Username must be no longer than 10 characters' | |
} | |
function handleSubmit(event) { | |
event.preventDefault() | |
setTouched(true) | |
if (!formIsValid) return | |
onSubmitUsername(username) | |
} | |
function handleChange(event) { | |
setUsername(event.currentTarget.value) | |
} | |
function handleBlur() { | |
setTouched(true) | |
} | |
return ( | |
<form name="usernameForm" onSubmit={handleSubmit} noValidate> | |
<div> | |
<label htmlFor="usernameInput">Username:</label> | |
<input | |
ref={usernameInputRef} | |
id="usernameInput" | |
type="text" | |
value={username} | |
onChange={handleChange} | |
onBlur={handleBlur} | |
pattern="[a-z]{3,10}" | |
required | |
aria-describedby={displayErrorMessage ? 'error-message' : undefined} | |
/> | |
</div> | |
{displayErrorMessage ? ( | |
<div role="alert" className="error-message"> | |
{errorMessage} | |
</div> | |
) : null} | |
<button type="submit">Submit</button> | |
</form> | |
) | |
} | |
ReactDOM.render(<UsernameForm />, document.querySelector('#root'), () => { | |
const input = document.querySelector('#usernameInput') | |
input.focus() | |
input.blur() | |
timeout() | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
document.querySelector('button').click() | |
}) | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
}) | |
}) | |
function timeout() { | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve() | |
}, 0); | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://epicreact.dev/why-you-shouldnt-put-refs-in-a-dependency-array/ | |
import * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
function UsernameForm({ | |
initialUsername = '', | |
onSubmitUsername, | |
}) { | |
// region setup | |
const [username, setUsername] = React.useState(initialUsername) | |
const [touched, setTouched] = React.useState(false) | |
const usernameInputRef = React.useRef(null) | |
// endregion | |
// region user state | |
const usernameIsLowerCase = username === username.toLowerCase() | |
const usernameIsLongEnough = username.length >= 3 | |
const usernameIsShortEnough = username.length <= 10 | |
const formIsValid = | |
usernameIsShortEnough && usernameIsLongEnough && usernameIsLowerCase | |
// endregion | |
// region effects | |
const displayErrorMessage = touched && !formIsValid | |
React.useEffect(() => { | |
if (displayErrorMessage) usernameInputRef.current?.focus() | |
}, [displayErrorMessage]) | |
// endregion | |
// region error message state | |
let errorMessage = null | |
if (!usernameIsLowerCase) { | |
errorMessage = 'Username must be lower case' | |
} else if (!usernameIsLongEnough) { | |
errorMessage = 'Username must be at least 3 characters long' | |
} else if (!usernameIsShortEnough) { | |
errorMessage = 'Username must be no longer than 10 characters' | |
} | |
// endregion | |
// region handlers | |
function handleSubmit(event) { | |
event.preventDefault() | |
setTouched(true) | |
if (!formIsValid) return | |
onSubmitUsername(username) | |
} | |
function handleChange(event) { | |
setUsername(event.currentTarget.value) | |
} | |
function handleBlur() { | |
setTouched(true) | |
} | |
// endregion | |
return ( | |
<form name="usernameForm" onSubmit={handleSubmit} noValidate> | |
<div> | |
<label htmlFor="usernameInput">Username:</label> | |
<input | |
ref={usernameInputRef} | |
id="usernameInput" | |
type="text" | |
value={username} | |
onChange={handleChange} | |
onBlur={handleBlur} | |
pattern="[a-z]{3,10}" | |
required | |
aria-describedby={displayErrorMessage ? 'error-message' : undefined} | |
/> | |
</div> | |
{displayErrorMessage ? ( | |
<div role="alert" className="error-message"> | |
{errorMessage} | |
</div> | |
) : null} | |
<button type="submit">Submit</button> | |
</form> | |
) | |
} | |
ReactDOM.render(<UsernameForm />, document.querySelector('#root'), () => { | |
const input = document.querySelector('#usernameInput') | |
input.focus() | |
input.blur() | |
timeout() | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
document.querySelector('button').click() | |
}) | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
}) | |
}) | |
function timeout() { | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve() | |
}, 0); | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://epicreact.dev/why-you-shouldnt-put-refs-in-a-dependency-array/ | |
import * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
class UsernameForm extends React.Component { | |
constructor(props) { | |
super(props) | |
this.state = { | |
touched: false, | |
...this.getDerivedUserState(this.props.initialUsername || '') | |
} | |
this.usernameInputRef = React.createRef(); | |
} | |
getDerivedUserState(username) { | |
const usernameIsLowerCase = username === username.toLowerCase() | |
const usernameIsLongEnough = username.length >= 3 | |
const usernameIsShortEnough = username.length <= 10 | |
const formIsValid = | |
usernameIsShortEnough && usernameIsLongEnough && usernameIsLowerCase | |
return { | |
username, | |
usernameIsShortEnough, | |
usernameIsLongEnough, | |
usernameIsLowerCase, | |
formIsValid | |
} | |
} | |
shouldDisplayErrorMessage() { | |
const {touched, formIsValid} = this.state | |
return touched && !formIsValid | |
} | |
componentDidUpdate() { | |
if (this.shouldDisplayErrorMessage()) { | |
this.usernameInputRef.current.focus() | |
} | |
} | |
getErrorMessage() { | |
const {usernameIsLowerCase, usernameIsLongEnough, usernameIsShortEnough} = this.state | |
if (!usernameIsLowerCase) { | |
return 'Username must be lower case' | |
} else if (!usernameIsLongEnough) { | |
return 'Username must be at least 3 characters long' | |
} else if (!usernameIsShortEnough) { | |
return 'Username must be no longer than 10 characters' | |
} | |
return null | |
} | |
handleBlur() { | |
this.setState({touched: true}) | |
} | |
handleChange(event) { | |
this.setState( | |
this.getDerivedUserState( | |
event.currentTarget.value | |
) | |
) | |
} | |
handleSubmit(event) { | |
event.preventDefault() | |
const {formIsValid, username} = this.state | |
this.setState({touched: true}) | |
if (!formIsValid) return | |
this.props.onSubmitUsername(username) | |
} | |
render() { | |
const {username} = this.state | |
const displayErrorMessage = this.shouldDisplayErrorMessage() | |
return ( | |
<form name="usernameForm" onSubmit={e => this.handleSubmit(e)} noValidate> | |
<div> | |
<label htmlFor="usernameInput">Username:</label> | |
<input | |
ref={this.usernameInputRef} | |
id="usernameInput" | |
type="text" | |
value={username} | |
onChange={e => this.handleChange(e)} | |
onBlur={() => this.handleBlur()} | |
pattern="[a-z]{3,10}" | |
required | |
aria-describedby={displayErrorMessage ? 'error-message' : undefined} | |
/> | |
</div> | |
{displayErrorMessage ? ( | |
<div role="alert" className="error-message"> | |
{this.getErrorMessage()} | |
</div> | |
) : null} | |
<button type="submit">Submit</button> | |
</form> | |
) | |
} | |
} | |
ReactDOM.render(<UsernameForm />, document.querySelector('#root'), () => { | |
const input = document.querySelector('#usernameInput') | |
input.focus() | |
input.blur() | |
timeout() | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
document.querySelector('button').click() | |
}) | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
}) | |
}) | |
function timeout() { | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve() | |
}, 0); | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://epicreact.dev/why-you-shouldnt-put-refs-in-a-dependency-array/ | |
import * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
class UsernameForm extends React.Component { | |
constructor(props) { | |
super(props) | |
this.state = { | |
touched: false, | |
...this.getDerivedUserState(this.props.initialUsername || '') | |
} | |
this.usernameInputRef = React.createRef(); | |
} | |
render() { | |
const {username} = this.state | |
const displayErrorMessage = this.shouldDisplayErrorMessage() | |
return ( | |
<form name="usernameForm" onSubmit={e => this.handleSubmit(e)} noValidate> | |
<div> | |
<label htmlFor="usernameInput">Username:</label> | |
<input | |
ref={this.usernameInputRef} | |
id="usernameInput" | |
type="text" | |
value={username} | |
onBlur={() => this.handleBlur()} | |
onChange={e => this.handleChange(e)} | |
pattern="[a-z]{3,10}" | |
required | |
aria-describedby={displayErrorMessage ? 'error-message' : undefined} | |
/> | |
</div> | |
{displayErrorMessage ? ( | |
<div role="alert" className="error-message"> | |
{this.getErrorMessage()} | |
</div> | |
) : null} | |
<button type="submit">Submit</button> | |
</form> | |
) | |
} | |
shouldDisplayErrorMessage() { | |
const {touched, formIsValid} = this.state | |
return touched && !formIsValid | |
} | |
handleSubmit(event) { | |
event.preventDefault() | |
const {formIsValid, username} = this.state | |
this.setState({touched: true}) | |
if (!formIsValid) return | |
this.props.onSubmitUsername(username) | |
} | |
handleBlur() { | |
this.setState({touched: true}) | |
} | |
handleChange(event) { | |
this.setState( | |
this.getDerivedUserState( | |
event.currentTarget.value | |
) | |
) | |
} | |
getDerivedUserState(username) { | |
const usernameIsLowerCase = username === username.toLowerCase() | |
const usernameIsLongEnough = username.length >= 3 | |
const usernameIsShortEnough = username.length <= 10 | |
const formIsValid = | |
usernameIsShortEnough && usernameIsLongEnough && usernameIsLowerCase | |
return { | |
username, | |
usernameIsShortEnough, | |
usernameIsLongEnough, | |
usernameIsLowerCase, | |
formIsValid | |
} | |
} | |
getErrorMessage() { | |
const {usernameIsLowerCase, usernameIsLongEnough, usernameIsShortEnough} = this.state | |
if (!usernameIsLowerCase) { | |
return 'Username must be lower case' | |
} else if (!usernameIsLongEnough) { | |
return 'Username must be at least 3 characters long' | |
} else if (!usernameIsShortEnough) { | |
return 'Username must be no longer than 10 characters' | |
} | |
return null | |
} | |
componentDidUpdate() { | |
if (this.shouldDisplayErrorMessage()) { | |
this.usernameInputRef.current.focus() | |
} | |
} | |
} | |
ReactDOM.render(<UsernameForm />, document.querySelector('#root'), () => { | |
const input = document.querySelector('#usernameInput') | |
input.focus() | |
input.blur() | |
timeout() | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
document.querySelector('button').click() | |
}) | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
}) | |
}) | |
function timeout() { | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve() | |
}, 0); | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
function UsernameForm({ | |
initialUsername = '', | |
onSubmitUsername, | |
}) { | |
const usernameInputRef = React.useRef(null) | |
function render() { | |
return ( | |
<form name="usernameForm" onSubmit={handleSubmit} noValidate> | |
<div> | |
<label htmlFor="usernameInput">Username:</label> | |
<input | |
ref={usernameInputRef} | |
id="usernameInput" | |
type="text" | |
value={username} | |
onBlur={handleBlur} | |
onChange={handleChange} | |
pattern="[a-z]{3,10}" | |
required | |
aria-describedby={displayErrorMessage ? 'error-message' : undefined} | |
/> | |
</div> | |
{displayErrorMessage ? ( | |
<div role="alert" className="error-message"> | |
{errorMessage} | |
</div> | |
) : null} | |
<button type="submit">Submit</button> | |
</form> | |
) | |
} | |
function handleSubmit(event) { | |
event.preventDefault() | |
setTouched(true) | |
if (!isFormValid()) return | |
onSubmitUsername(username) | |
} | |
function handleBlur() { | |
setTouched(true) | |
} | |
function handleChange(event) { | |
setUsername(event.currentTarget.value) | |
} | |
function isFormValid() { | |
return usernameIsShortEnough && usernameIsLongEnough && usernameIsLowerCase | |
} | |
const [touched, setTouched] = React.useState(false) | |
const [username, setUsername] = React.useState(initialUsername) | |
const usernameIsLowerCase = username === username.toLowerCase() | |
const usernameIsLongEnough = username.length >= 3 | |
const usernameIsShortEnough = username.length <= 10 | |
const displayErrorMessage = touched && !isFormValid() | |
let errorMessage = null | |
if (!usernameIsLowerCase) { | |
errorMessage = 'Username must be lower case' | |
} else if (!usernameIsLongEnough) { | |
errorMessage = 'Username must be at least 3 characters long' | |
} else if (!usernameIsShortEnough) { | |
errorMessage = 'Username must be no longer than 10 characters' | |
} | |
React.useEffect(() => { | |
if (displayErrorMessage) usernameInputRef.current?.focus() | |
}, [displayErrorMessage]) | |
return render() | |
} | |
ReactDOM.render(<UsernameForm />, document.querySelector('#root'), () => { | |
const input = document.querySelector('#usernameInput') | |
input.focus() | |
input.blur() | |
timeout() | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
document.querySelector('button').click() | |
}) | |
.then(() => { | |
console.log(document.querySelector('.error-message')?.textContent) | |
}) | |
}) | |
function timeout() { | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve() | |
}, 0); | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment