fix(bubble-input): maintain focus by handling blur events

Use forwardRef to expose the content editable element and let the parent component control the focus

fix #1 #2
This commit is contained in:
Takuya Matsuyama 2022-05-13 16:15:28 +09:00
parent 5fba2eceaa
commit 5b86470066
3 changed files with 28 additions and 19 deletions

View file

@ -1,4 +1,4 @@
import { useState, useCallback, useEffect } from 'react' import { useState, useCallback } from 'react'
import './App.css' import './App.css'
import Chat from './chat' import Chat from './chat'
import Bubble from './bubble' import Bubble from './bubble'
@ -17,13 +17,6 @@ function App() {
} }
}, [newMessage, messages]) }, [newMessage, messages])
useEffect(() => {
const el = document.querySelector('.bubble.input > div')
if (el) {
el.focus()
}
}, [])
return ( return (
<div className="App"> <div className="App">
<Chat> <Chat>

View file

@ -1,8 +1,9 @@
import React, { useCallback, useState } from 'react' import React, { useCallback, useState, useRef, useEffect } from 'react'
import './bubble-input.css' import './bubble-input.css'
import ContentEditable from './content-editable' import ContentEditable from './content-editable'
const BubbleInput = ({ onChange, onSubmit, value }) => { const BubbleInput = ({ onChange, onSubmit, value }) => {
const refEditable = useRef()
const [submitted, setSubmitted] = useState(false) const [submitted, setSubmitted] = useState(false)
const handleChange = useCallback( const handleChange = useCallback(
@ -25,7 +26,15 @@ const BubbleInput = ({ onChange, onSubmit, value }) => {
}, },
[onSubmit] [onSubmit]
) )
console.log('value:', value) const handleBlur = useCallback(() => {
const { current: elDiv } = refEditable
if (elDiv) {
elDiv.focus()
}
}, [refEditable])
useEffect(() => handleBlur(), [handleBlur])
return ( return (
<div <div
className={`bubble input ${value.length === 0 ? 'empty' : ''} ${ className={`bubble input ${value.length === 0 ? 'empty' : ''} ${
@ -33,8 +42,10 @@ const BubbleInput = ({ onChange, onSubmit, value }) => {
}`} }`}
> >
<ContentEditable <ContentEditable
ref={refEditable}
onChange={handleChange} onChange={handleChange}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onBlur={handleBlur}
value={value} value={value}
/> />
</div> </div>

View file

@ -1,17 +1,14 @@
import * as React from 'react' import * as React from 'react'
class ContentEditable extends React.Component { class ContentEditable extends React.Component {
constructor(props) {
super(props)
this.refElement = React.createRef()
}
render() { render() {
const { innerRef } = this.props
return ( return (
<div <div
key={Math.random()} key={Math.random()}
ref={this.refElement} ref={innerRef}
onInput={this.emitChange} onInput={this.emitChange}
onBlur={this.emitChange} onBlur={this.handleBlur}
onKeyDown={this.emitKeyDown} onKeyDown={this.emitKeyDown}
contentEditable contentEditable
spellCheck="false" spellCheck="false"
@ -21,12 +18,12 @@ class ContentEditable extends React.Component {
} }
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
const { current: div } = this.refElement const { current: div } = this.props.innerRef
return nextProps.value !== div.innerText return nextProps.value !== div.innerText
} }
emitChange = () => { emitChange = () => {
const { current: div } = this.refElement const { current: div } = this.props.innerRef
var value = div.innerText var value = div.innerText
if (this.props.onChange && value !== this.lastValue) { if (this.props.onChange && value !== this.lastValue) {
this.props.onChange({ this.props.onChange({
@ -42,6 +39,14 @@ class ContentEditable extends React.Component {
const { onKeyDown } = this.props const { onKeyDown } = this.props
onKeyDown && onKeyDown(e) onKeyDown && onKeyDown(e)
} }
handleBlur = e => {
this.emitKeyDown(e)
const { onBlur } = this.props
onBlur && onBlur(e)
}
} }
export default ContentEditable export default React.forwardRef((props, ref) => {
return <ContentEditable innerRef={ref} {...props} />
})