refactor: Convert Comments class to functional using hooks

This commit is contained in:
2025-07-07 17:40:02 +00:00
committed by Tanner Collin
parent 26a6353ca5
commit 034c440e46

View File

@@ -1,35 +1,32 @@
import React from 'react';
import { Link } from 'react-router-dom';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useParams } from 'react-router-dom';
import { HashLink } from 'react-router-hash-link';
import { Helmet } from 'react-helmet';
import moment from 'moment';
import localForage from 'localforage';
import { infoLine, ToggleDot } from './utils.js';
class Comments extends React.Component {
constructor(props) {
super(props);
function countComments(c) {
return c.comments.reduce((sum, x) => sum + countComments(x), 1);
}
const id = this.props.match.params.id;
const cache = this.props.cache;
function Comments({ cache }) {
const { id } = useParams();
if (id in cache) console.log('cache hit');
this.state = {
story: cache[id] || false,
error: false,
collapsed: [],
expanded: [],
};
}
componentDidMount() {
const id = this.props.match.params.id;
const [story, setStory] = useState(cache[id] || false);
const [error, setError] = useState(false);
const [collapsed, setCollapsed] = useState([]);
const [expanded, setExpanded] = useState([]);
useEffect(() => {
localForage.getItem(id)
.then(
(value) => {
this.setState({ story: value });
if (value) {
setStory(value);
}
}
);
@@ -37,47 +34,41 @@ class Comments extends React.Component {
.then(res => res.json())
.then(
(result) => {
this.setState({ story: result.story }, () => {
setStory(result.story);
localForage.setItem(id, result.story);
const hash = window.location.hash.substring(1);
if (hash) {
document.getElementById(hash).scrollIntoView();
setTimeout(() => {
const element = document.getElementById(hash);
if (element) {
element.scrollIntoView();
}
}, 0);
}
});
localForage.setItem(id, result.story);
},
(error) => {
this.setState({ error: true });
setError(true);
}
);
}
}, [id]);
collapseComment(cid) {
this.setState(prevState => ({
...prevState,
collapsed: [...prevState.collapsed, cid],
expanded: prevState.expanded.filter(x => x !== cid),
}));
}
const collapseComment = useCallback((cid) => {
setCollapsed(prev => [...prev, cid]);
setExpanded(prev => prev.filter(x => x !== cid));
}, []);
expandComment(cid) {
this.setState(prevState => ({
...prevState,
collapsed: prevState.collapsed.filter(x => x !== cid),
expanded: [...prevState.expanded, cid],
}));
}
const expandComment = useCallback((cid) => {
setCollapsed(prev => prev.filter(x => x !== cid));
setExpanded(prev => [...prev, cid]);
}, []);
countComments(c) {
return c.comments.reduce((sum, x) => sum + this.countComments(x), 1);
}
displayComment(story, c, level) {
const displayComment = useCallback((story, c, level) => {
const cid = c.author+c.date;
const collapsed = this.state.collapsed.includes(cid);
const expanded = this.state.expanded.includes(cid);
const isCollapsed = collapsed.includes(cid);
const isExpanded = expanded.includes(cid);
const hidden = collapsed || (level == 4 && !expanded);
const hidden = isCollapsed || (level == 4 && !isExpanded);
const hasChildren = c.comments.length !== 0;
return (
@@ -88,26 +79,21 @@ class Comments extends React.Component {
{' '} | <HashLink to={'#'+cid} id={cid}>{moment.unix(c.date).fromNow()}</HashLink>
{hidden || hasChildren &&
<span className='collapser pointer' onClick={() => this.collapseComment(cid)}></span>
<span className='collapser pointer' onClick={() => collapseComment(cid)}></span>
}
</p>
</div>
<div className={collapsed ? 'text hidden' : 'text'} dangerouslySetInnerHTML={{ __html: c.text }} />
<div className={isCollapsed ? 'text hidden' : 'text'} dangerouslySetInnerHTML={{ __html: c.text }} />
{hidden && hasChildren ?
<div className='comment lined info pointer' onClick={() => this.expandComment(cid)}>[show {this.countComments(c)-1} more]</div>
<div className='comment lined info pointer' onClick={() => expandComment(cid)}>[show {countComments(c)-1} more]</div>
:
c.comments.map(i => this.displayComment(story, i, level + 1))
c.comments.map(i => displayComment(story, i, level + 1))
}
</div>
);
}
render() {
const id = this.props.match.params.id;
const story = this.state.story;
const error = this.state.error;
}, [collapsed, expanded, collapseComment, expandComment]);
return (
<div className='container'>
@@ -128,7 +114,7 @@ class Comments extends React.Component {
{infoLine(story)}
<div className='comments'>
{story.comments.map(c => this.displayComment(story, c, 0))}
{story.comments.map(c => displayComment(story, c, 0))}
</div>
</div>
:
@@ -137,7 +123,6 @@ class Comments extends React.Component {
<ToggleDot id={id} article={true} />
</div>
);
}
}
export default Comments;