Skip to content

Commit 2dd01a4

Browse files
committed
[React-hooks] Update components to use hooks just to try them
1 parent b90208e commit 2dd01a4

File tree

8 files changed

+268
-402
lines changed

8 files changed

+268
-402
lines changed

src/components/ListBoxWithSearch.jsx

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,47 @@
1-
import React from 'react'
1+
import React, { useState } from 'react'
22

33
import ListBox from './ListBox'
4+
45
import { regExpEscape } from '../misc/reqExp'
56

6-
class ListBoxWithSearch extends React.PureComponent {
7-
constructor(props) {
8-
super(props)
9-
this.state = {
10-
searchQuery: null,
11-
}
12-
}
7+
const ListBoxWithSearch = props => {
8+
let { items } = props
139

14-
onSearch = e => {
15-
this.setState({ searchQuery: e.target.value.trim() })
10+
const [ searchQuery, setSearchQuery ] = useState(null)
11+
12+
const onSearch = e => {
13+
setSearchQuery(e.target.value.trim())
1614
}
1715

18-
render() {
19-
const { searchQuery } = this.state
20-
let { items } = this.props
21-
22-
// Normalize items arrays so each item is always an object.
23-
items = items.map((item) => {
24-
if (item !== Object(item)) {
25-
return { name: item, value: item }
26-
}
27-
return item
28-
})
29-
30-
// Filter out only those items that match search query. If no query is
31-
// set, do nothing and use the entire set.
32-
if (searchQuery) {
33-
const regExp = new RegExp(regExpEscape(searchQuery), 'gi')
34-
items = items.filter(item => item.name.match(regExp))
16+
// Normalize items arrays so each item is always an object.
17+
items = items.map((item) => {
18+
if (item !== Object(item)) {
19+
return { name: item, value: item }
3520
}
3621

37-
return (
38-
[
39-
<div className="new-snippet-lang-header" key="Syntax input">
40-
<input className="input" placeholder="Type to search..." onChange={this.onSearch} />
41-
</div>,
42-
<div className="new-snippet-lang-list-wrapper" key="Syntax list">
43-
<ListBox
44-
items={items}
45-
onClick={this.props.onClick}
46-
/>
47-
</div>,
48-
]
49-
)
22+
return item
23+
})
24+
25+
// Filter out only those items that match search query. If no query is
26+
// set, do nothing and use the entire set.
27+
if (searchQuery) {
28+
const regExp = new RegExp(regExpEscape(searchQuery), 'gi')
29+
items = items.filter(item => item.name.match(regExp))
5030
}
31+
32+
return (
33+
[
34+
<div className="new-snippet-lang-header" key="Syntax input">
35+
<input className="input" placeholder="Type to search..." onChange={onSearch} />
36+
</div>,
37+
<div className="new-snippet-lang-list-wrapper" key="Syntax list">
38+
<ListBox
39+
items={items}
40+
onClick={props.onClick}
41+
/>
42+
</div>,
43+
]
44+
)
5145
}
5246

5347
export default ListBoxWithSearch

src/components/NewSnippet.jsx

Lines changed: 98 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react'
1+
import React, { useEffect, useState } from 'react'
22
import { connect } from 'react-redux'
33
import AceEditor from 'react-ace'
44
import { WithContext as Tags } from 'react-tag-input'
@@ -7,166 +7,150 @@ import 'brace/theme/textmate'
77

88
import Notification from './common/Notification'
99
import ListBoxWithSearch from './ListBoxWithSearch'
10+
1011
import { fetchSyntaxes, postSnippet } from '../actions'
1112

12-
import { validateSnippet } from '../entries/snippetValidation'
13-
import { getCurrentModeName, getModesByName } from '../misc/modes'
1413
import { onEditorLoad } from '../misc/editor'
15-
import { recalcLangHeaderHeight } from '../misc/dom'
14+
import { getCurrentModeName, getModesByName } from '../misc/modes'
1615

16+
import { validateSnippet } from '../entries/snippetValidation'
1717
import { delimeterKeys } from '../entries/keyboardKeys'
1818
import { defaultOptions } from '../entries/aceEditorOptions'
1919

2020
import '../styles/NewSnippet.styl'
2121

22-
class NewSnippet extends React.Component {
23-
constructor(props) {
24-
super(props)
22+
const recalcLangHeaderHeight = () => {
23+
const mainHeader = 'new-snippet-code-header'
24+
const langHeader = 'new-snippet-lang-header'
2525

26-
this.state = {
27-
content: '',
28-
title: '',
29-
tags: [],
30-
syntax: '',
31-
validationError: null,
32-
}
33-
}
26+
const height = document.getElementsByClassName(mainHeader)[0].offsetHeight
3427

35-
componentDidMount() {
36-
const { dispatch } = this.props
28+
document.getElementsByClassName(langHeader)[0].setAttribute('style', `height:${height}px`)
29+
}
30+
31+
const NewSnippet = props => {
32+
const { dispatch, history } = props
33+
34+
const [ syntax, setSyntax ] = useState('')
35+
const [ content, setContent ] = useState('')
36+
const [ title, setTitle ] = useState('')
37+
const [ tags, setTags ] = useState([])
38+
const [ validationError, setValidationError ] = useState(null)
39+
40+
useEffect(() => {
3741
dispatch(fetchSyntaxes)
38-
}
42+
}, [])
43+
44+
useEffect(() => {
45+
recalcLangHeaderHeight()
46+
}, [tags])
3947

40-
onTagAdded = tag => {
48+
const onTagAdded = tag => {
4149
if (tag && tag.text) {
42-
this.setState({ tags: [...this.state.tags, tag] }, () => {
43-
recalcLangHeaderHeight()
44-
})
50+
setTags([...tags, tag])
4551
}
4652
}
4753

48-
onTagRemoved = i => {
49-
const { tags } = this.state
50-
51-
this.setState({ tags: tags.filter((tag, index) => index !== i) }, () => {
52-
recalcLangHeaderHeight()
53-
})
54+
const onTagRemoved = i => {
55+
setTags(tags.filter((tag, index) => index !== i))
5456
}
5557

56-
onTagBlur = tag => {
57-
this.onTagAdded({ id: tag, text: tag })
58+
const onTagBlur = tag => {
59+
onTagAdded({ id: tag, text: tag })
5860
}
5961

60-
onSyntaxClick = syntax => {
61-
this.setState({ syntax })
62+
const onSyntaxClick = syntax => {
63+
setSyntax(syntax)
6264
}
6365

64-
onInputChange = e => {
65-
const { name, value } = e.target
66+
const onTitleChange = e => {
67+
const { value } = e.target
6668

67-
this.setState({ [name]: value })
69+
setTitle(value)
6870
}
6971

70-
validate = () => {
71-
const { content } = this.state
72-
73-
return validateSnippet({ content: content.trim() })
74-
}
72+
const validate = () => validateSnippet({ content: content.trim() })
7573

76-
post = e => {
74+
const post = e => {
7775
e.preventDefault()
78-
const { dispatch, history } = this.props
79-
const { error } = this.validate()
76+
const { error } = validate()
8077

81-
this.setState({ validationError: error })
78+
setValidationError(error)
8279

8380
if (!error) {
84-
const {
85-
content, title, tags, syntax,
86-
} = this.state
87-
8881
dispatch(postSnippet({
8982
content, title, tags: tags.map(tag => tag.text), syntax,
9083
}, json => history.push(`/${json.id}`)))
9184
}
9285
}
9386

94-
getSyntaxes = () => {
87+
const getSyntaxes = () => {
9588
const { modesByName } = getModesByName()
9689

97-
return this.props.syntaxes.map(item => ({
90+
return props.syntaxes.map(item => ({
9891
name: modesByName[item].caption,
9992
value: item,
10093
}))
10194
}
10295

103-
renderValidationError = () => {
104-
const { validationError } = this.state
105-
106-
return validationError && <Notification
107-
message="Content is required :("
108-
show={!!validationError}
109-
/>
110-
}
111-
112-
render() {
113-
const { syntax, content, title, tags } = this.state
114-
115-
return (
116-
<form
117-
className="new-snippet"
118-
key="New Snippet"
119-
onSubmit={this.post}
120-
role="presentation"
121-
>
122-
<div className="new-snippet-code-wrapper">
123-
<div className="new-snippet-code-header">
124-
<input
125-
className="input"
126-
placeholder="Title"
127-
name="title"
128-
type="text"
129-
value={title}
130-
onChange={this.onInputChange}
131-
/>
132-
<Tags
133-
placeholder="Tags"
134-
tags={tags}
135-
handleDelete={this.onTagRemoved}
136-
handleAddition={this.onTagAdded}
137-
handleInputBlur={this.onTagBlur}
138-
delimiters={delimeterKeys}
139-
/>
140-
</div>
141-
<div className="new-snippet-code">
142-
<AceEditor
143-
mode={getCurrentModeName(syntax)}
144-
width="100%"
145-
height="100%"
146-
focus
147-
theme="textmate"
148-
onLoad={onEditorLoad}
149-
setOptions={defaultOptions}
150-
editorProps={{ $blockScrolling: Infinity }}
151-
value={content}
152-
onChange={(content) => { this.setState({ content }) }}
153-
/>
154-
155-
<div className="new-snippet-code-bottom-bar">
156-
{this.renderValidationError()}
157-
<input type="submit" value="POST SNIPPET" />
158-
</div>
159-
</div>
96+
const renderValidationError = () => (validationError && <Notification
97+
message="Content is required :("
98+
show={!!validationError}
99+
/>)
100+
101+
return (
102+
<form
103+
className="new-snippet"
104+
key="New Snippet"
105+
onSubmit={post}
106+
role="presentation"
107+
>
108+
<div className="new-snippet-code-wrapper">
109+
<div className="new-snippet-code-header">
110+
<input
111+
className="input"
112+
placeholder="Title"
113+
name="title"
114+
type="text"
115+
value={title}
116+
onChange={onTitleChange}
117+
/>
118+
<Tags
119+
placeholder="Tags"
120+
tags={tags}
121+
handleDelete={onTagRemoved}
122+
handleAddition={onTagAdded}
123+
handleInputBlur={onTagBlur}
124+
delimiters={delimeterKeys}
125+
/>
160126
</div>
161-
<div className="new-snippet-lang-wrapper">
162-
<ListBoxWithSearch
163-
items={this.getSyntaxes()}
164-
onClick={this.onSyntaxClick}
127+
<div className="new-snippet-code">
128+
<AceEditor
129+
mode={getCurrentModeName(syntax)}
130+
width="100%"
131+
height="100%"
132+
focus
133+
theme="textmate"
134+
onLoad={onEditorLoad}
135+
setOptions={defaultOptions}
136+
editorProps={{ $blockScrolling: Infinity }}
137+
value={content}
138+
onChange={(value) => setContent(value)}
165139
/>
140+
<div className="new-snippet-code-bottom-bar">
141+
{renderValidationError()}
142+
<input type="submit" value="POST SNIPPET" />
143+
</div>
166144
</div>
167-
</form>
168-
)
169-
}
145+
</div>
146+
<div className="new-snippet-lang-wrapper">
147+
<ListBoxWithSearch
148+
items={getSyntaxes()}
149+
onClick={onSyntaxClick}
150+
/>
151+
</div>
152+
</form>
153+
)
170154
}
171155

172156
export default connect(state => ({

src/components/RecentSnippetItem.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react'
22
import { Link } from 'react-router-dom'
33

4-
import { getCurrentModeCaption } from '../misc/modes'
54
import { downloadSnippet } from '../misc/download'
5+
import { getCurrentModeCaption } from '../misc/modes'
66
import { getSnippetTitle, formatDate } from '../misc/snippet'
77
import { getRawUrl } from '../misc/url'
88

0 commit comments

Comments
 (0)